Bug 1267890 part 1 - Add @supports -moz-bool-pref() support for stylo. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Thu, 07 Dec 2017 22:31:41 -0600
changeset 709729 3ff0c3e0e12673b0fbae87592c39cae04c37932a
parent 709728 a64d09ceaa119e702caa763810429122313cbafd
child 709730 7420fe1babf529eac00e5c3b2a40ca2459e6273d
push id92735
push userxquan@mozilla.com
push dateFri, 08 Dec 2017 17:27:30 +0000
reviewersemilio
bugs1267890
milestone59.0a1
Bug 1267890 part 1 - Add @supports -moz-bool-pref() support for stylo. r?emilio MozReview-Commit-ID: C9Pq2zLLaGp
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
servo/components/style/gecko/generated/bindings.rs
servo/components/style/stylesheets/supports_rule.rs
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -53,16 +53,17 @@
 
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/GeckoStyleContext.h"
 #include "mozilla/Keyframe.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/ServoElementSnapshot.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/SizeOfState.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/ServoMediaList.h"
 #include "mozilla/RWLock.h"
 #include "mozilla/dom/Element.h"
@@ -2827,8 +2828,15 @@ Gecko_ContentList_AppendAll(
 const nsTArray<Element*>*
 Gecko_GetElementsWithId(const nsIDocument* aDocument, nsAtom* aId)
 {
   MOZ_ASSERT(aDocument);
   MOZ_ASSERT(aId);
 
   return aDocument->GetAllElementsForId(nsDependentAtomString(aId));
 }
+
+bool
+Gecko_GetBoolPrefValue(const char* aPrefName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return Preferences::GetBool(aPrefName);
+}
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -710,11 +710,15 @@ void Gecko_ReportUnexpectedCSSError(mozi
 void Gecko_ContentList_AppendAll(nsSimpleContentList* aContentList,
                                  const RawGeckoElement** aElements,
                                  size_t aLength);
 
 const nsTArray<mozilla::dom::Element*>* Gecko_GetElementsWithId(
     const nsIDocument* aDocument,
     nsAtom* aId);
 
+// Check the value of the given bool preference. The pref name needs to
+// be null-terminated.
+bool Gecko_GetBoolPrefValue(const char* pref_name);
+
 } // extern "C"
 
 #endif // mozilla_ServoBindings_h
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -1590,9 +1590,11 @@ extern "C" {
 } extern "C" {
  pub fn Gecko_DestroyCSSErrorReporter ( reporter : * mut ErrorReporter , ) ; 
 } extern "C" {
  pub fn Gecko_ReportUnexpectedCSSError ( reporter : * mut ErrorReporter , message : * const :: std :: os :: raw :: c_char , param : * const :: std :: os :: raw :: c_char , paramLen : u32 , prefix : * const :: std :: os :: raw :: c_char , prefixParam : * const :: std :: os :: raw :: c_char , prefixParamLen : u32 , suffix : * const :: std :: os :: raw :: c_char , source : * const :: std :: os :: raw :: c_char , sourceLen : u32 , lineNumber : u32 , colNumber : u32 , ) ; 
 } extern "C" {
  pub fn Gecko_ContentList_AppendAll ( aContentList : * mut nsSimpleContentList , aElements : * mut * const RawGeckoElement , aLength : usize , ) ; 
 } extern "C" {
  pub fn Gecko_GetElementsWithId ( aDocument : * const nsIDocument , aId : * mut nsAtom , ) -> * const nsTArray < * mut Element > ; 
+} extern "C" {
+ pub fn Gecko_GetBoolPrefValue ( pref_name : * const :: std :: os :: raw :: c_char , ) -> bool ; 
 }
\ No newline at end of file
--- a/servo/components/style/stylesheets/supports_rule.rs
+++ b/servo/components/style/stylesheets/supports_rule.rs
@@ -8,19 +8,22 @@ use cssparser::{Delimiter, parse_importa
 use cssparser::{ParseError as CssParseError, ParserInput};
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
 use parser::ParserContext;
 use properties::{PropertyId, PropertyDeclaration, SourcePropertyDeclaration};
 use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use std::ascii::AsciiExt;
+use std::ffi::{CStr, CString};
 use std::fmt;
+use std::str;
 use style_traits::{ToCss, ParseError};
-use stylesheets::{CssRuleType, CssRules};
+use stylesheets::{CssRuleType, CssRules, Origin};
 
 /// An [`@supports`][supports] rule.
 ///
 /// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports
 #[derive(Debug)]
 pub struct SupportsRule {
     /// The parsed condition
     pub condition: SupportsCondition,
@@ -78,16 +81,20 @@ pub enum SupportsCondition {
     /// `(condition)`
     Parenthesized(Box<SupportsCondition>),
     /// `(condition) and (condition) and (condition) ..`
     And(Vec<SupportsCondition>),
     /// `(condition) or (condition) or (condition) ..`
     Or(Vec<SupportsCondition>),
     /// `property-ident: value` (value can be any tokens)
     Declaration(Declaration),
+    /// `-moz-bool-pref("pref-name")`
+    /// Since we need to pass it through FFI to get the pref value,
+    /// we store it as CString directly.
+    MozBoolPref(CString),
     /// `(any tokens)` or `func(any tokens)`
     FutureSyntax(String),
 }
 
 impl SupportsCondition {
     /// Parse a condition
     ///
     /// <https://drafts.csswg.org/css-conditional/#supports_condition>
@@ -140,36 +147,70 @@ impl SupportsCondition {
             Token::ParenthesisBlock => {
                 let nested = input.try(|input| {
                     input.parse_nested_block(|i| parse_condition_or_declaration(i))
                 });
                 if nested.is_ok() {
                     return nested;
                 }
             }
-            Token::Function(_) => {}
+            Token::Function(ident) => {
+                // Although this is an internal syntax, it is not necessary to check
+                // parsing context as far as we accept any unexpected token as future
+                // syntax, and evaluate it to false when not in chrome / ua sheet.
+                // See https://drafts.csswg.org/css-conditional-3/#general_enclosed
+                if ident.eq_ignore_ascii_case("-moz-bool-pref") {
+                    if let Ok(name) = input.try(|i| {
+                        i.parse_nested_block(|i| {
+                            i.expect_string()
+                                .map(|s| s.to_string())
+                                .map_err(CssParseError::<()>::from)
+                        }).and_then(|s| {
+                            CString::new(s)
+                                .map_err(|_| location.new_custom_error(()))
+                        })
+                    }) {
+                        return Ok(SupportsCondition::MozBoolPref(name));
+                    }
+                }
+            }
             t => return Err(location.new_unexpected_token_error(t)),
         }
         input.parse_nested_block(|i| consume_any_value(i))?;
         Ok(SupportsCondition::FutureSyntax(input.slice_from(pos).to_owned()))
     }
 
     /// Evaluate a supports condition
     pub fn eval(&self, cx: &ParserContext) -> bool {
         match *self {
             SupportsCondition::Not(ref cond) => !cond.eval(cx),
             SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
             SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
             SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
             SupportsCondition::Declaration(ref decl) => decl.eval(cx),
+            SupportsCondition::MozBoolPref(ref name) => eval_moz_bool_pref(name, cx),
             SupportsCondition::FutureSyntax(_) => false
         }
     }
 }
 
+#[cfg(feature = "gecko")]
+fn eval_moz_bool_pref(name: &CStr, cx: &ParserContext) -> bool {
+    use gecko_bindings::bindings;
+    if cx.stylesheet_origin != Origin::UserAgent && !cx.chrome_rules_enabled() {
+        return false;
+    }
+    unsafe { bindings::Gecko_GetBoolPrefValue(name.as_ptr()) }
+}
+
+#[cfg(feature = "servo")]
+fn eval_moz_bool_pref(_: &str, _: &ParserContext) -> bool {
+    false
+}
+
 /// supports_condition | declaration
 /// <https://drafts.csswg.org/css-conditional/#dom-css-supports-conditiontext-conditiontext>
 pub fn parse_condition_or_declaration<'i, 't>(input: &mut Parser<'i, 't>)
                                               -> Result<SupportsCondition, ParseError<'i>> {
     if let Ok(condition) = input.try(SupportsCondition::parse) {
         Ok(SupportsCondition::Parenthesized(Box::new(condition)))
     } else {
         Declaration::parse(input).map(SupportsCondition::Declaration)
@@ -212,16 +253,23 @@ impl ToCss for SupportsCondition {
                 }
                 Ok(())
             }
             SupportsCondition::Declaration(ref decl) => {
                 dest.write_str("(")?;
                 decl.to_css(dest)?;
                 dest.write_str(")")
             }
+            SupportsCondition::MozBoolPref(ref name) => {
+                dest.write_str("-moz-bool-pref(")?;
+                let name = str::from_utf8(name.as_bytes())
+                    .expect("Should be parsed from valid UTF-8");
+                name.to_css(dest)?;
+                dest.write_str(")")
+            }
             SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),
         }
     }
 }
 
 #[derive(Clone, Debug)]
 /// A possibly-invalid property declaration
 pub struct Declaration(pub String);