1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:37:46 +00:00

LibWeb: Support :active pseudo-class for hyperlinks, :focus possibly

Adds support for the :active pseudo-class for hyperlinks (<a> tags
only).

Also, since it was very similar to :focus and an element having a
focused state was already implemented, I went ahead and implemented
that pseudo-class too, although I cannot come up with a working
example to validate it.
This commit is contained in:
Paul Irwin 2021-06-18 16:42:34 -06:00 committed by Andreas Kling
parent 457edaa4d2
commit 5eb65286b6
10 changed files with 39 additions and 2 deletions

View file

@ -563,6 +563,8 @@ public:
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link;
} else if (pseudo_name.equals_ignoring_case("visited")) { } else if (pseudo_name.equals_ignoring_case("visited")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited;
} else if (pseudo_name.equals_ignoring_case("active")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Active;
} else if (pseudo_name.equals_ignoring_case("hover")) { } else if (pseudo_name.equals_ignoring_case("hover")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover;
} else if (pseudo_name.equals_ignoring_case("focus")) { } else if (pseudo_name.equals_ignoring_case("focus")) {

View file

@ -200,6 +200,8 @@ Vector<CSS::Selector::ComplexSelector> Parser::parse_selectors(Vector<String> pa
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link;
} else if (pseudo_name.equals_ignoring_case("visited")) { } else if (pseudo_name.equals_ignoring_case("visited")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited;
} else if (pseudo_name.equals_ignoring_case("active")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Active;
} else if (pseudo_name.equals_ignoring_case("hover")) { } else if (pseudo_name.equals_ignoring_case("hover")) {
simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover; simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover;
} else if (pseudo_name.equals_ignoring_case("focus")) { } else if (pseudo_name.equals_ignoring_case("focus")) {

View file

@ -43,6 +43,7 @@ public:
Enabled, Enabled,
Checked, Checked,
Not, Not,
Active,
}; };
PseudoClass pseudo_class { PseudoClass::None }; PseudoClass pseudo_class { PseudoClass::None };

View file

@ -44,13 +44,18 @@ static bool matches(const CSS::Selector::SimpleSelector& component, const DOM::E
case CSS::Selector::SimpleSelector::PseudoClass::Visited: case CSS::Selector::SimpleSelector::PseudoClass::Visited:
// FIXME: Maybe match this selector sometimes? // FIXME: Maybe match this selector sometimes?
return false; return false;
case CSS::Selector::SimpleSelector::PseudoClass::Active:
if (!element.is_active())
return false;
break;
case CSS::Selector::SimpleSelector::PseudoClass::Hover: case CSS::Selector::SimpleSelector::PseudoClass::Hover:
if (!matches_hover_pseudo_class(element)) if (!matches_hover_pseudo_class(element))
return false; return false;
break; break;
case CSS::Selector::SimpleSelector::PseudoClass::Focus: case CSS::Selector::SimpleSelector::PseudoClass::Focus:
// FIXME: Implement matches_focus_pseudo_class(element) if (!element.is_focused())
return false; return false;
break;
case CSS::Selector::SimpleSelector::PseudoClass::FirstChild: case CSS::Selector::SimpleSelector::PseudoClass::FirstChild:
if (element.previous_element_sibling()) if (element.previous_element_sibling())
return false; return false;

View file

@ -876,6 +876,17 @@ void Document::set_focused_element(Element* element)
m_layout_root->set_needs_display(); m_layout_root->set_needs_display();
} }
void Document::set_active_element(Element* element)
{
if (m_active_element == element)
return;
m_active_element = element;
if (m_layout_root)
m_layout_root->set_needs_display();
}
void Document::set_ready_state(const String& ready_state) void Document::set_ready_state(const String& ready_state)
{ {
m_ready_state = ready_state; m_ready_state = ready_state;

View file

@ -208,6 +208,10 @@ public:
void set_focused_element(Element*); void set_focused_element(Element*);
const Element* active_element() const { return m_active_element; }
void set_active_element(Element*);
bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; } bool created_for_appropriate_template_contents() const { return m_created_for_appropriate_template_contents; }
void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; } void set_created_for_appropriate_template_contents(bool value) { m_created_for_appropriate_template_contents = value; }
@ -323,6 +327,7 @@ private:
bool m_editable { false }; bool m_editable { false };
WeakPtr<Element> m_focused_element; WeakPtr<Element> m_focused_element;
WeakPtr<Element> m_active_element;
bool m_created_for_appropriate_template_contents { false }; bool m_created_for_appropriate_template_contents { false };
RefPtr<Document> m_associated_inert_template_document; RefPtr<Document> m_associated_inert_template_document;

View file

@ -327,6 +327,11 @@ bool Element::is_focused() const
return document().focused_element() == this; return document().focused_element() == this;
} }
bool Element::is_active() const
{
return document().active_element() == this;
}
NonnullRefPtr<HTMLCollection> Element::get_elements_by_tag_name(FlyString const& tag_name) NonnullRefPtr<HTMLCollection> Element::get_elements_by_tag_name(FlyString const& tag_name)
{ {
// FIXME: Support "*" for tag_name // FIXME: Support "*" for tag_name

View file

@ -86,6 +86,8 @@ public:
bool is_focused() const; bool is_focused() const;
virtual bool is_focusable() const { return false; } virtual bool is_focusable() const { return false; }
bool is_active() const;
NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&); NonnullRefPtr<HTMLCollection> get_elements_by_tag_name(FlyString const&);
NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&); NonnullRefPtr<HTMLCollection> get_elements_by_class_name(FlyString const&);

View file

@ -341,6 +341,9 @@ void dump_selector(StringBuilder& builder, const CSS::Selector& selector)
case CSS::Selector::SimpleSelector::PseudoClass::Visited: case CSS::Selector::SimpleSelector::PseudoClass::Visited:
pseudo_class_description = "Visited"; pseudo_class_description = "Visited";
break; break;
case CSS::Selector::SimpleSelector::PseudoClass::Active:
pseudo_class_description = "Active";
break;
case CSS::Selector::SimpleSelector::PseudoClass::None: case CSS::Selector::SimpleSelector::PseudoClass::None:
pseudo_class_description = "None"; pseudo_class_description = "None";
break; break;

View file

@ -237,6 +237,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
auto anchor = href.substring_view(1, href.length() - 1); auto anchor = href.substring_view(1, href.length() - 1);
m_frame.scroll_to_anchor(anchor); m_frame.scroll_to_anchor(anchor);
} else { } else {
document->set_active_element(link);
if (m_frame.is_top_level()) { if (m_frame.is_top_level()) {
if (auto* page = m_frame.page()) if (auto* page = m_frame.page())
page->client().page_did_click_link(url, link->target(), modifiers); page->client().page_did_click_link(url, link->target(), modifiers);