Browse Source

LibWeb: Implement `:any-link` and `:local-link` pseudo-class selectors

`:any-link` matches links, whether they have been visited or not.

`:local-link` matches links to the current URL.
Sam Atkins 1 year ago
parent
commit
3af8b491b4

+ 1 - 0
Tests/LibWeb/Ref/css-any-link-selector-ref.html

@@ -0,0 +1 @@
+<a href="example.com" style="color: orange">Link</a>

+ 6 - 0
Tests/LibWeb/Ref/css-any-link-selector.html

@@ -0,0 +1,6 @@
+<style>
+    :any-link {
+        color: orange;
+    }
+</style>
+<a href="example.com">Link</a>

+ 2 - 0
Tests/LibWeb/Ref/css-local-link-selector-ref.html

@@ -0,0 +1,2 @@
+<a href="" style="color: orange">Local</a>
+<a href="example.com">Not local</a>

+ 7 - 0
Tests/LibWeb/Ref/css-local-link-selector.html

@@ -0,0 +1,7 @@
+<style>
+    :local-link {
+        color: orange;
+    }
+</style>
+<a href="">Local</a>
+<a href="example.com">Not local</a>

+ 2 - 0
Tests/LibWeb/Ref/manifest.json

@@ -5,8 +5,10 @@
     "square-flex.html": "square-ref.html",
     "separate-borders-inline-table.html": "separate-borders-ref.html",
     "opacity-stacking.html": "opacity-stacking-ref.html",
+    "css-any-link-selector.html": "css-any-link-selector-ref.html",
     "css-gradient-currentcolor.html": "css-gradient-currentcolor-ref.html",
     "css-lang-selector.html": "css-lang-selector-ref.html",
+    "css-local-link-selector.html": "css-local-link-selector-ref.html",
     "css-gradients.html": "css-gradients-ref.html",
     "svg-symbol.html": "svg-symbol-ref.html",
     "svg-gradient-spreadMethod.html": "svg-gradient-spreadMethod-ref.html",

+ 6 - 0
Userland/Libraries/LibWeb/CSS/PseudoClasses.json

@@ -2,6 +2,9 @@
   "active": {
     "argument": ""
   },
+  "any-link": {
+    "argument": ""
+  },
   "buffering": {
     "argument": ""
   },
@@ -62,6 +65,9 @@
   "link": {
     "argument": ""
   },
+  "local-link": {
+    "argument": ""
+  },
   "muted": {
     "argument": ""
   },

+ 17 - 0
Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp

@@ -213,7 +213,24 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
 {
     switch (pseudo_class.type) {
     case CSS::PseudoClass::Link:
+    case CSS::PseudoClass::AnyLink:
+        // NOTE: AnyLink should match whether the link is visited or not, so if we ever start matching
+        //       :visited, we'll need to handle these differently.
         return matches_link_pseudo_class(element);
+    case CSS::PseudoClass::LocalLink: {
+        // The :local-link pseudo-class allows authors to style hyperlinks based on the users current location
+        // within a site. It represents an element that is the source anchor of a hyperlink whose target’s
+        // absolute URL matches the element’s own document URL. If the hyperlink’s target includes a fragment
+        // URL, then the fragment URL of the current URL must also match; if it does not, then the fragment
+        // URL portion of the current URL is not taken into account in the comparison.
+        if (!matches_link_pseudo_class(element))
+            return false;
+        auto document_url = element.document().url();
+        AK::URL target_url = element.document().parse_url(element.attribute(HTML::AttributeNames::href));
+        if (target_url.fragment().has_value())
+            return document_url.equals(target_url, AK::URL::ExcludeFragment::No);
+        return document_url.equals(target_url, AK::URL::ExcludeFragment::Yes);
+    }
     case CSS::PseudoClass::Visited:
         // FIXME: Maybe match this selector sometimes?
         return false;