Serenity Operating System
at portability 148 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 <LibHTML/CSS/SelectorEngine.h> 28#include <LibHTML/DOM/Document.h> 29#include <LibHTML/DOM/Element.h> 30#include <LibHTML/DOM/Text.h> 31 32namespace SelectorEngine { 33 34static bool matches_hover_pseudo_class(const Element& element) 35{ 36 auto* hovered_node = element.document().hovered_node(); 37 if (!hovered_node) 38 return false; 39 if (&element == hovered_node) 40 return true; 41 return element.is_ancestor_of(*hovered_node); 42} 43 44bool matches(const Selector::SimpleSelector& component, const Element& element) 45{ 46 switch (component.pseudo_class) { 47 case Selector::SimpleSelector::PseudoClass::None: 48 break; 49 case Selector::SimpleSelector::PseudoClass::Link: 50 if (!element.is_link()) 51 return false; 52 break; 53 case Selector::SimpleSelector::PseudoClass::Hover: 54 if (!matches_hover_pseudo_class(element)) 55 return false; 56 break; 57 case Selector::SimpleSelector::PseudoClass::FirstChild: 58 if (element.previous_element_sibling()) 59 return false; 60 break; 61 case Selector::SimpleSelector::PseudoClass::LastChild: 62 if (element.next_element_sibling()) 63 return false; 64 break; 65 case Selector::SimpleSelector::PseudoClass::OnlyChild: 66 if (element.previous_element_sibling() || element.next_element_sibling()) 67 return false; 68 break; 69 case Selector::SimpleSelector::PseudoClass::Empty: 70 if (element.first_child_of_type<Element>() || element.first_child_of_type<Text>()) 71 return false; 72 break; 73 } 74 75 switch (component.attribute_match_type) { 76 case Selector::SimpleSelector::AttributeMatchType::HasAttribute: 77 if (!element.has_attribute(component.attribute_name)) 78 return false; 79 break; 80 case Selector::SimpleSelector::AttributeMatchType::ExactValueMatch: 81 if (element.attribute(component.attribute_name) != component.attribute_value) 82 return false; 83 break; 84 default: 85 break; 86 } 87 88 switch (component.type) { 89 case Selector::SimpleSelector::Type::Universal: 90 return true; 91 case Selector::SimpleSelector::Type::Id: 92 return component.value == element.attribute("id"); 93 case Selector::SimpleSelector::Type::Class: 94 return element.has_class(component.value); 95 case Selector::SimpleSelector::Type::TagName: 96 return component.value == element.tag_name(); 97 default: 98 ASSERT_NOT_REACHED(); 99 } 100} 101 102bool matches(const Selector& selector, int component_list_index, const Element& element) 103{ 104 auto& component_list = selector.complex_selectors()[component_list_index]; 105 for (auto& component : component_list.compound_selector) { 106 if (!matches(component, element)) 107 return false; 108 } 109 switch (component_list.relation) { 110 case Selector::ComplexSelector::Relation::None: 111 return true; 112 case Selector::ComplexSelector::Relation::Descendant: 113 ASSERT(component_list_index != 0); 114 for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) { 115 if (!is<Element>(*ancestor)) 116 continue; 117 if (matches(selector, component_list_index - 1, to<Element>(*ancestor))) 118 return true; 119 } 120 return false; 121 case Selector::ComplexSelector::Relation::ImmediateChild: 122 ASSERT(component_list_index != 0); 123 if (!element.parent() || !is<Element>(*element.parent())) 124 return false; 125 return matches(selector, component_list_index - 1, to<Element>(*element.parent())); 126 case Selector::ComplexSelector::Relation::AdjacentSibling: 127 ASSERT(component_list_index != 0); 128 if (auto* sibling = element.previous_element_sibling()) 129 return matches(selector, component_list_index - 1, *sibling); 130 return false; 131 case Selector::ComplexSelector::Relation::GeneralSibling: 132 ASSERT(component_list_index != 0); 133 for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { 134 if (matches(selector, component_list_index - 1, *sibling)) 135 return true; 136 } 137 return false; 138 } 139 ASSERT_NOT_REACHED(); 140} 141 142bool matches(const Selector& selector, const Element& element) 143{ 144 ASSERT(!selector.complex_selectors().is_empty()); 145 return matches(selector, selector.complex_selectors().size() - 1, element); 146} 147 148}