From 3af8b491b425985fbd3ee3f90fb1c7c43e87c824 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 22 Aug 2023 15:24:12 +0100 Subject: [PATCH] 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. --- Tests/LibWeb/Ref/css-any-link-selector-ref.html | 1 + Tests/LibWeb/Ref/css-any-link-selector.html | 6 ++++++ .../LibWeb/Ref/css-local-link-selector-ref.html | 2 ++ Tests/LibWeb/Ref/css-local-link-selector.html | 7 +++++++ Tests/LibWeb/Ref/manifest.json | 2 ++ .../Libraries/LibWeb/CSS/PseudoClasses.json | 6 ++++++ .../Libraries/LibWeb/CSS/SelectorEngine.cpp | 17 +++++++++++++++++ 7 files changed, 41 insertions(+) create mode 100644 Tests/LibWeb/Ref/css-any-link-selector-ref.html create mode 100644 Tests/LibWeb/Ref/css-any-link-selector.html create mode 100644 Tests/LibWeb/Ref/css-local-link-selector-ref.html create mode 100644 Tests/LibWeb/Ref/css-local-link-selector.html diff --git a/Tests/LibWeb/Ref/css-any-link-selector-ref.html b/Tests/LibWeb/Ref/css-any-link-selector-ref.html new file mode 100644 index 00000000000..c52dc9d82a3 --- /dev/null +++ b/Tests/LibWeb/Ref/css-any-link-selector-ref.html @@ -0,0 +1 @@ +Link diff --git a/Tests/LibWeb/Ref/css-any-link-selector.html b/Tests/LibWeb/Ref/css-any-link-selector.html new file mode 100644 index 00000000000..bc20a79168b --- /dev/null +++ b/Tests/LibWeb/Ref/css-any-link-selector.html @@ -0,0 +1,6 @@ + +Link diff --git a/Tests/LibWeb/Ref/css-local-link-selector-ref.html b/Tests/LibWeb/Ref/css-local-link-selector-ref.html new file mode 100644 index 00000000000..44d033ca803 --- /dev/null +++ b/Tests/LibWeb/Ref/css-local-link-selector-ref.html @@ -0,0 +1,2 @@ +Local +Not local diff --git a/Tests/LibWeb/Ref/css-local-link-selector.html b/Tests/LibWeb/Ref/css-local-link-selector.html new file mode 100644 index 00000000000..e9fbbdfd6f0 --- /dev/null +++ b/Tests/LibWeb/Ref/css-local-link-selector.html @@ -0,0 +1,7 @@ + +Local +Not local diff --git a/Tests/LibWeb/Ref/manifest.json b/Tests/LibWeb/Ref/manifest.json index 09c36b5436f..60d30ac5b29 100644 --- a/Tests/LibWeb/Ref/manifest.json +++ b/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", diff --git a/Userland/Libraries/LibWeb/CSS/PseudoClasses.json b/Userland/Libraries/LibWeb/CSS/PseudoClasses.json index 5d2826acc6c..25f68d921d6 100644 --- a/Userland/Libraries/LibWeb/CSS/PseudoClasses.json +++ b/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": "" }, diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp index 2d16da1ccf2..e7a25e9d7a7 100644 --- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/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;