Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <LibWeb/CSS/SelectorEngine.h>
28#include <LibWeb/DOM/Document.h>
29#include <LibWeb/DOM/Element.h>
30#include <LibWeb/DOM/Text.h>
31
32namespace Web {
33
34namespace SelectorEngine {
35
36static bool matches_hover_pseudo_class(const Element& element)
37{
38 auto* hovered_node = element.document().hovered_node();
39 if (!hovered_node)
40 return false;
41 if (&element == hovered_node)
42 return true;
43 return element.is_ancestor_of(*hovered_node);
44}
45
46bool matches(const Selector::SimpleSelector& component, const Element& element)
47{
48 switch (component.pseudo_class) {
49 case Selector::SimpleSelector::PseudoClass::None:
50 break;
51 case Selector::SimpleSelector::PseudoClass::Link:
52 if (!element.is_link())
53 return false;
54 break;
55 case Selector::SimpleSelector::PseudoClass::Hover:
56 if (!matches_hover_pseudo_class(element))
57 return false;
58 break;
59 case Selector::SimpleSelector::PseudoClass::FirstChild:
60 if (element.previous_element_sibling())
61 return false;
62 break;
63 case Selector::SimpleSelector::PseudoClass::LastChild:
64 if (element.next_element_sibling())
65 return false;
66 break;
67 case Selector::SimpleSelector::PseudoClass::OnlyChild:
68 if (element.previous_element_sibling() || element.next_element_sibling())
69 return false;
70 break;
71 case Selector::SimpleSelector::PseudoClass::Empty:
72 if (element.first_child_of_type<Element>() || element.first_child_of_type<Text>())
73 return false;
74 break;
75 }
76
77 switch (component.attribute_match_type) {
78 case Selector::SimpleSelector::AttributeMatchType::HasAttribute:
79 if (!element.has_attribute(component.attribute_name))
80 return false;
81 break;
82 case Selector::SimpleSelector::AttributeMatchType::ExactValueMatch:
83 if (element.attribute(component.attribute_name) != component.attribute_value)
84 return false;
85 break;
86 default:
87 break;
88 }
89
90 switch (component.type) {
91 case Selector::SimpleSelector::Type::Universal:
92 return true;
93 case Selector::SimpleSelector::Type::Id:
94 return component.value == element.attribute("id");
95 case Selector::SimpleSelector::Type::Class:
96 return element.has_class(component.value);
97 case Selector::SimpleSelector::Type::TagName:
98 return component.value == element.tag_name();
99 default:
100 ASSERT_NOT_REACHED();
101 }
102}
103
104bool matches(const Selector& selector, int component_list_index, const Element& element)
105{
106 auto& component_list = selector.complex_selectors()[component_list_index];
107 for (auto& component : component_list.compound_selector) {
108 if (!matches(component, element))
109 return false;
110 }
111 switch (component_list.relation) {
112 case Selector::ComplexSelector::Relation::None:
113 return true;
114 case Selector::ComplexSelector::Relation::Descendant:
115 ASSERT(component_list_index != 0);
116 for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
117 if (!is<Element>(*ancestor))
118 continue;
119 if (matches(selector, component_list_index - 1, to<Element>(*ancestor)))
120 return true;
121 }
122 return false;
123 case Selector::ComplexSelector::Relation::ImmediateChild:
124 ASSERT(component_list_index != 0);
125 if (!element.parent() || !is<Element>(*element.parent()))
126 return false;
127 return matches(selector, component_list_index - 1, to<Element>(*element.parent()));
128 case Selector::ComplexSelector::Relation::AdjacentSibling:
129 ASSERT(component_list_index != 0);
130 if (auto* sibling = element.previous_element_sibling())
131 return matches(selector, component_list_index - 1, *sibling);
132 return false;
133 case Selector::ComplexSelector::Relation::GeneralSibling:
134 ASSERT(component_list_index != 0);
135 for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
136 if (matches(selector, component_list_index - 1, *sibling))
137 return true;
138 }
139 return false;
140 }
141 ASSERT_NOT_REACHED();
142}
143
144bool matches(const Selector& selector, const Element& element)
145{
146 ASSERT(!selector.complex_selectors().is_empty());
147 return matches(selector, selector.complex_selectors().size() - 1, element);
148}
149
150}
151
152}