瀏覽代碼

LibWeb: Support obsolete but required -webkit- CSS parsing quirk

As outlined in: https://www.w3.org/TR/selectors-4/#compat

We now do not treat unknown webkit pseudo-elements as invalid at parse
time, and also support serializing these elements.

Fixes: #21959
Shannon Booth 1 年之前
父節點
當前提交
ed97946975

+ 12 - 0
Tests/LibWeb/Ref/css-functional-unknown-webkit-pseudo-element.html

@@ -0,0 +1,12 @@
+<!doctype html>
+<link rel="match" href="reference/text-div.html" />
+<style>
+  * {
+    visibility: visible;
+  }
+  .d-none,
+  .oops::-webkit-jkl() {
+    visibility: hidden;
+  }
+</style>
+<div class="d-none">Well, hello friends!</div>

+ 12 - 0
Tests/LibWeb/Ref/css-unknown-webkit-pseudo-element.html

@@ -0,0 +1,12 @@
+<!doctype html>
+<link rel="match" href="reference/text-div.html" />
+<style>
+  * {
+    visibility: hidden;
+  }
+  .d-none,
+  .oops::-webkit-asdf {
+    visibility: visible;
+  }
+</style>
+<div class="d-none">Well, hello friends!</div>

+ 1 - 0
Tests/LibWeb/Text/expected/css/unknown-webkit-pseudo-element-serialization.txt

@@ -0,0 +1 @@
+   .d-none, .oops::-webkit-asdf

+ 14 - 0
Tests/LibWeb/Text/input/css/unknown-webkit-pseudo-element-serialization.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html><style type="text/css">
+  .d-none,
+  .oops::-webkit-asDF {
+    display: none !important;
+  }
+</style><div class="d-none">should not be displayed</div>
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        let rules = document.styleSheets[0].cssRules;
+        let rule = rules.item(0);
+        println(rule.selectorText);
+    });
+</script>

+ 14 - 0
Userland/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp

@@ -10,6 +10,7 @@
 
 #include <AK/Debug.h>
 #include <LibWeb/CSS/Parser/Parser.h>
+#include <LibWeb/Infra/Strings.h>
 
 namespace Web::CSS::Parser {
 
@@ -361,6 +362,19 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
             };
         }
 
+        // https://www.w3.org/TR/selectors-4/#compat
+        // All other pseudo-elements whose names begin with the string “-webkit-” (matched ASCII case-insensitively)
+        // and that are not functional notations must be treated as valid at parse time. (That is, ::-webkit-asdf is
+        // valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
+        // must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
+        if (pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
+            return Selector::SimpleSelector {
+                .type = Selector::SimpleSelector::Type::PseudoElement,
+                // Unknown -webkit- pseudo-elements must be serialized in ASCII lowercase.
+                .value = Selector::PseudoElement { Selector::PseudoElement::Type::UnknownWebKit, MUST(Infra::to_ascii_lowercase(pseudo_name.to_string())) },
+            };
+        }
+
         if (has_ignored_vendor_prefix(pseudo_name))
             return ParseError::IncludesIgnoredVendorPrefix;
 

+ 3 - 1
Userland/Libraries/LibWeb/CSS/Selector.cpp

@@ -390,8 +390,10 @@ StringView Selector::PseudoElement::name(Selector::PseudoElement::Type pseudo_el
         return "placeholder"sv;
     case Selector::PseudoElement::Type::Selection:
         return "selection"sv;
-    case Selector::PseudoElement::Type::PseudoElementCount:
+    case Selector::PseudoElement::Type::KnownPseudoElementCount:
         break;
+    case Selector::PseudoElement::Type::UnknownWebKit:
+        VERIFY_NOT_REACHED();
     }
     VERIFY_NOT_REACHED();
 }

+ 17 - 2
Userland/Libraries/LibWeb/CSS/Selector.h

@@ -39,15 +39,26 @@ public:
             Selection,
 
             // Keep this last.
-            PseudoElementCount,
+            KnownPseudoElementCount,
+
+            // https://www.w3.org/TR/selectors-4/#compat
+            // NOTE: This is not last as the 'unknown -webkit- pseudo-elements' are not stored as part of any Element.
+            UnknownWebKit,
         };
 
         explicit PseudoElement(Type type)
             : m_type(type)
+        {
+            VERIFY(type != Type::UnknownWebKit);
+        }
+
+        PseudoElement(Type type, String name)
+            : m_type(type)
+            , m_name(move(name))
         {
         }
 
-        constexpr bool operator==(PseudoElement const&) const = default;
+        bool operator==(PseudoElement const&) const = default;
 
         static Optional<PseudoElement> from_string(FlyString const&);
 
@@ -55,6 +66,9 @@ public:
 
         StringView name() const
         {
+            if (!m_name.is_empty())
+                return m_name;
+
             return name(m_type);
         }
 
@@ -62,6 +76,7 @@ public:
 
     private:
         Type m_type;
+        String m_name;
     };
 
     struct SimpleSelector {

+ 2 - 2
Userland/Libraries/LibWeb/DOM/Element.h

@@ -410,7 +410,7 @@ private:
     RefPtr<CSS::StyleProperties> m_computed_css_values;
     HashMap<FlyString, CSS::StyleProperty> m_custom_properties;
 
-    using PseudoElementCustomProperties = Array<HashMap<FlyString, CSS::StyleProperty>, to_underlying(CSS::Selector::PseudoElement::Type::PseudoElementCount)>;
+    using PseudoElementCustomProperties = Array<HashMap<FlyString, CSS::StyleProperty>, to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount)>;
     mutable OwnPtr<PseudoElementCustomProperties> m_pseudo_element_custom_properties;
     PseudoElementCustomProperties& pseudo_element_custom_properties() const;
 
@@ -421,7 +421,7 @@ private:
 
     Optional<FlyString> m_id;
 
-    using PseudoElementLayoutNodes = Array<JS::GCPtr<Layout::Node>, to_underlying(CSS::Selector::PseudoElement::Type::PseudoElementCount)>;
+    using PseudoElementLayoutNodes = Array<JS::GCPtr<Layout::Node>, to_underlying(CSS::Selector::PseudoElement::Type::KnownPseudoElementCount)>;
     OwnPtr<PseudoElementLayoutNodes> m_pseudo_element_nodes;
 
     // https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reaction-queue

+ 1 - 1
Userland/Libraries/LibWeb/Internals/Inspector.cpp

@@ -41,7 +41,7 @@ void Inspector::inspect_dom_node(i32 node_id, Optional<i32> const& pseudo_elemen
 {
     auto& page = global_object().browsing_context()->page();
     page.client().inspector_did_select_dom_node(node_id, pseudo_element.map([](auto value) {
-        VERIFY(value < to_underlying(Web::CSS::Selector::PseudoElement::Type::PseudoElementCount));
+        VERIFY(value < to_underlying(Web::CSS::Selector::PseudoElement::Type::KnownPseudoElementCount));
         return static_cast<Web::CSS::Selector::PseudoElement::Type>(value);
     }));
 }