Serenity Operating System
at hosted 152 lines 5.9 kB view raw
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}