Bug 1424106 - Accept unknown webkit pseudo-element. r=emilio
authorXidorn Quan <me@upsuper.org>
Fri, 24 Aug 2018 22:17:37 +1000
changeset 433294 988b2e838fd1c3c335169808b563ee6c07feeac9
parent 433293 d4cb5a2697e5298202436b0a29f7240a5b8d5149
child 433295 2102f887e46bf3cb2b6a7fb00ed33640c9cb330e
push id68253
push userxquan@mozilla.com
push dateFri, 24 Aug 2018 12:18:25 +0000
treeherderautoland@988b2e838fd1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1424106
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1424106 - Accept unknown webkit pseudo-element. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D4186
modules/libpref/init/StaticPrefList.h
servo/components/style/gecko/pseudo_element.rs
servo/components/style/gecko/pseudo_element_definition.mako.rs
servo/components/style/stylist.rs
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/compat/webkit-pseudo-element.html.ini
testing/web-platform/tests/compat/webkit-pseudo-element.html
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -436,16 +436,23 @@ VARCACHE_PREF(
 
 // Does overflow-break: break-word affect intrinsic size?
 VARCACHE_PREF(
   "layout.css.overflow-break.intrinsic-size",
    layout_css_overflow_break_intrinsic_size,
   bool, false
 )
 
+// Does arbitrary ::-webkit-* pseudo-element parsed?
+VARCACHE_PREF(
+  "layout.css.unknown-webkit-pseudo-element",
+   layout_css_unknown_webkit_pseudo_element,
+  bool, false
+)
+
 //---------------------------------------------------------------------------
 // JavaScript prefs
 //---------------------------------------------------------------------------
 
 // nsJSEnvironmentObserver observes the memory-pressure notifications and
 // forces a garbage collection and cycle collection when it happens, if the
 // appropriate pref is set.
 #ifdef ANDROID
--- a/servo/components/style/gecko/pseudo_element.rs
+++ b/servo/components/style/gecko/pseudo_element.rs
@@ -9,17 +9,17 @@
 //! need to update the checked-in files for Servo.
 
 use cssparser::ToCss;
 use gecko_bindings::structs::{self, CSSPseudoElementType};
 use properties::{ComputedValues, PropertyFlags};
 use properties::longhands::display::computed_value::T as Display;
 use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
 use std::fmt;
-use str::starts_with_ignore_ascii_case;
+use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
 use string_cache::Atom;
 use thin_slice::ThinBoxedSlice;
 use values::serialize_atom_identifier;
 
 include!(concat!(
     env!("OUT_DIR"),
     "/gecko/pseudo_element_definition.rs"
 ));
--- a/servo/components/style/gecko/pseudo_element_definition.mako.rs
+++ b/servo/components/style/gecko/pseudo_element_definition.mako.rs
@@ -8,16 +8,19 @@ pub enum PseudoElement {
     % for pseudo in PSEUDOS:
         /// ${pseudo.value}
         % if pseudo.is_tree_pseudo_element():
         ${pseudo.capitalized_pseudo()}(ThinBoxedSlice<Atom>),
         % else:
         ${pseudo.capitalized_pseudo()},
         % endif
     % endfor
+    /// ::-webkit-* that we don't recognize
+    /// https://github.com/whatwg/compat/issues/103
+    UnknownWebkit(Atom),
 }
 
 /// Important: If you change this, you should also update Gecko's
 /// nsCSSPseudoElements::IsEagerlyCascadedInServo.
 <% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %>
 <% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %>
 <% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if not pseudo.is_tree_pseudo_element()] %>
 
@@ -42,31 +45,33 @@ pub const EAGER_PSEUDOS: [PseudoElement;
 
 <%def name="pseudo_element_variant(pseudo, tree_arg='..')">\
 PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\
 </%def>
 
 impl PseudoElement {
     /// Get the pseudo-element as an atom.
     #[inline]
-    pub fn atom(&self) -> Atom {
+    fn atom(&self) -> Atom {
         match *self {
             % for pseudo in PSEUDOS:
                 ${pseudo_element_variant(pseudo)} => atom!("${pseudo.value}"),
             % endfor
+            PseudoElement::UnknownWebkit(..) => unreachable!(),
         }
     }
 
     /// Returns an index of the pseudo-element.
     #[inline]
     pub fn index(&self) -> usize {
         match *self {
             % for i, pseudo in enumerate(PSEUDOS):
             ${pseudo_element_variant(pseudo)} => ${i},
             % endfor
+            PseudoElement::UnknownWebkit(..) => unreachable!(),
         }
     }
 
     /// Returns an array of `None` values.
     ///
     /// FIXME(emilio): Integer generics can't come soon enough.
     pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
         [
@@ -100,16 +105,22 @@ impl PseudoElement {
         match *self {
             % for pseudo in TREE_PSEUDOS:
             ${pseudo_element_variant(pseudo)} => true,
             % endfor
             _ => false,
         }
     }
 
+    /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element.
+    #[inline]
+    pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
+        matches!(*self, PseudoElement::UnknownWebkit(..))
+    }
+
     /// Gets the flags associated to this pseudo-element, or 0 if it's an
     /// anonymous box.
     pub fn flags(&self) -> u32 {
         match *self {
             % for pseudo in PSEUDOS:
                 ${pseudo_element_variant(pseudo)} =>
                 % if pseudo.is_tree_pseudo_element():
                     if unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_tree_pseudos_content_enabled } {
@@ -118,16 +129,17 @@ impl PseudoElement {
                         structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME
                     },
                 % elif pseudo.is_anon_box():
                     structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS,
                 % else:
                     structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident},
                 % endif
             % endfor
+            PseudoElement::UnknownWebkit(..) => 0,
         }
     }
 
     /// Construct a pseudo-element from a `CSSPseudoElementType`.
     #[inline]
     pub fn from_pseudo_type(type_: CSSPseudoElementType) -> Option<Self> {
         match type_ {
             % for pseudo in PSEUDOS:
@@ -138,31 +150,32 @@ impl PseudoElement {
                 % endif
             % endfor
             _ => None,
         }
     }
 
     /// Construct a `CSSPseudoElementType` from a pseudo-element
     #[inline]
-    pub fn pseudo_type(&self) -> CSSPseudoElementType {
+    fn pseudo_type(&self) -> CSSPseudoElementType {
         use gecko_bindings::structs::CSSPseudoElementType_InheritingAnonBox;
 
         match *self {
             % for pseudo in PSEUDOS:
                 % if not pseudo.is_anon_box():
                     PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType::${pseudo.pseudo_ident},
                 % elif pseudo.is_tree_pseudo_element():
                     PseudoElement::${pseudo.capitalized_pseudo()}(..) => CSSPseudoElementType::XULTree,
                 % elif pseudo.is_inheriting_anon_box():
                     PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType_InheritingAnonBox,
                 % else:
                     PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType::NonInheritingAnonBox,
                 % endif
             % endfor
+            PseudoElement::UnknownWebkit(..) => unreachable!(),
         }
     }
 
     /// Get a PseudoInfo for a pseudo
     pub fn pseudo_info(&self) -> (*mut structs::nsAtom, CSSPseudoElementType) {
         (self.atom().as_ptr(), self.pseudo_type())
     }
 
@@ -240,16 +253,25 @@ impl PseudoElement {
             }
             "-moz-placeholder" => {
                 return Some(PseudoElement::Placeholder);
             }
             _ => {
                 if starts_with_ignore_ascii_case(name, "-moz-tree-") {
                     return PseudoElement::tree_pseudo_element(name, Box::new([]))
                 }
+                if unsafe {
+                    structs::StaticPrefs_sVarCache_layout_css_unknown_webkit_pseudo_element
+                } {
+                    const WEBKIT_PREFIX: &str = "-webkit-";
+                    if starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
+                        let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);
+                        return Some(PseudoElement::UnknownWebkit(part.into()));
+                    }
+                }
             }
         }
 
         None
     }
 
     /// Constructs a tree pseudo-element from the given name and arguments.
     /// "name" must start with "-moz-tree-".
@@ -270,16 +292,20 @@ impl PseudoElement {
 
 impl ToCss for PseudoElement {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_char(':')?;
         match *self {
             % for pseudo in PSEUDOS:
                 ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
             % endfor
+            PseudoElement::UnknownWebkit(ref atom) => {
+                dest.write_str(":-webkit-")?;
+                serialize_atom_identifier(atom, dest)?;
+            }
         }
         if let Some(args) = self.tree_pseudo_args() {
             if !args.is_empty() {
                 dest.write_char('(')?;
                 let mut iter = args.iter();
                 if let Some(first) = iter.next() {
                     serialize_atom_identifier(&first, dest)?;
                     for item in iter {
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1876,17 +1876,19 @@ struct ElementAndPseudoRules {
 impl ElementAndPseudoRules {
     #[inline(always)]
     fn insert(
         &mut self,
         rule: Rule,
         pseudo_element: Option<&PseudoElement>,
         quirks_mode: QuirksMode,
     ) -> Result<(), FailedAllocationError> {
-        debug_assert!(pseudo_element.map_or(true, |pseudo| !pseudo.is_precomputed()));
+        debug_assert!(pseudo_element.map_or(true, |pseudo| {
+            !pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element()
+        }));
 
         let map = match pseudo_element {
             None => &mut self.element_map,
             Some(pseudo) => self.pseudos_map
                 .get_or_insert_with(pseudo, || Box::new(SelectorMap::new())),
         };
 
         map.insert(rule, quirks_mode)
@@ -2189,16 +2191,19 @@ impl CascadeData {
                                         StyleSource::from_rule(locked.clone()),
                                         self.rules_source_order,
                                         CascadeLevel::UANormal,
                                         selector.specificity(),
                                         0,
                                     ));
                                 continue;
                             }
+                            if pseudo.is_unknown_webkit_pseudo_element() {
+                                continue;
+                            }
                         }
 
                         let hashes = AncestorHashes::new(&selector, quirks_mode);
 
                         let rule = Rule::new(
                             selector.clone(),
                             hashes,
                             locked.clone(),
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -324783,16 +324783,22 @@
     ]
    ],
    "compat/idlharness.window.js": [
     [
      "/compat/idlharness.window.html",
      {}
     ]
    ],
+   "compat/webkit-pseudo-element.html": [
+    [
+     "/compat/webkit-pseudo-element.html",
+     {}
+    ]
+   ],
    "compat/webkit-text-fill-color-currentColor.html": [
     [
      "/compat/webkit-text-fill-color-currentColor.html",
      {}
     ]
    ],
    "console/console-is-a-namespace.any.js": [
     [
@@ -434268,16 +434274,20 @@
   "compat/webkit-linear-gradient-line-right.html": [
    "0f1a4b9500c362b67430ccbc9caeb165ee5327f4",
    "reftest"
   ],
   "compat/webkit-linear-gradient-line-top.html": [
    "579d88220610e4f2ea7e884018b655cf1c5c8dca",
    "reftest"
   ],
+  "compat/webkit-pseudo-element.html": [
+   "8f69477489a8eee76a6b379f07dcbcafda539c5a",
+   "testharness"
+  ],
   "compat/webkit-text-fill-color-currentColor.html": [
    "f4912c93450edf03b43b220d205460cc82ef9ba2",
    "testharness"
   ],
   "compat/webkit-text-fill-color-property-001-ref.html": [
    "8d9b8cc22e77bd92873e9846780add24cbc61e32",
    "support"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/compat/webkit-pseudo-element.html.ini
@@ -0,0 +1,2 @@
+[webkit-pseudo-element.html]
+  prefs: [layout.css.unknown-webkit-pseudo-element:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/compat/webkit-pseudo-element.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>WebKit-prefixed pseudo-elements</title>
+<link rel="author" title="Xidorn Quan" href="https://www.upsuper.org">
+<link rel="author" title="Mozilla" href="https://www.mozilla.org">
+<link rel="help" href="https://compat.spec.whatwg.org/#css-webkit-pseudo-elements">
+<meta name="assert" content="WebKit-prefixed pseudo-elements should always be valid">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style id="style">
+#test {
+  color: rgb(255, 0, 0);
+}
+span::-webkit-something-invalid, #test, ::-WeBkIt-sOmEtHiNg-NoNeXiSt123 {
+  color: rgb(0, 255, 0);
+}
+::-webkitfoo, #test {
+  color: rgb(255, 0, 0);
+}
+</style>
+<body>
+<div id="test"></div>
+<script>
+  test(() => {
+    let elem = document.getElementById("test");
+    assert_equals(getComputedStyle(elem).color, "rgb(0, 255, 0)");
+  }, "rules include webkit-prefixed pseudo-element should be cascaded");
+
+  test(() => {
+    let sheet = document.getElementById("style").sheet;
+    assert_equals(sheet.cssRules[1].selectorText,
+                  "span::-webkit-something-invalid, " +
+                  "#test, ::-webkit-something-nonexist123");
+  }, "webkit-prefixed pseudo-element selectors should be accessible from CSSOM");
+
+  test(() => {
+    document.querySelector("span::-webkit-something-invalid");
+    document.querySelectorAll("span::-webkit-something-invalid");
+  }, "qS and qSA shouldn't throw exception");
+
+  test(() => {
+    let sheet = document.getElementById("style").sheet;
+    assert_equals(sheet.cssRules.length, 2);
+    assert_throws("SyntaxError", () => document.querySelector("span::-webkitfoo"));
+    assert_throws("SyntaxError", () => document.querySelectorAll("span::-webkitfoo"));
+  }, "webkit-prefix without dash is invalid");
+</script>
+</body>