| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  * modification, are permitted provided that the following conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 1. Redistributions of source code must retain the above copyright notice, this | 
					
						
							|  |  |  |  *    list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | 
					
						
							|  |  |  |  *    this list of conditions and the following disclaimer in the documentation | 
					
						
							|  |  |  |  *    and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
					
						
							|  |  |  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
					
						
							|  |  |  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
					
						
							|  |  |  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
					
						
							|  |  |  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 16:41:30 +02:00
										 |  |  | #include <AK/QuickSort.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:32:51 +01:00
										 |  |  | #include <LibWeb/CSS/SelectorEngine.h>
 | 
					
						
							|  |  |  | #include <LibWeb/CSS/StyleResolver.h>
 | 
					
						
							|  |  |  | #include <LibWeb/CSS/StyleSheet.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Document.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Element.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Dump.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Parser/CSSParser.h>
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  | #include <ctype.h>
 | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2019-06-27 17:47:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | namespace Web { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 17:47:59 +02:00
										 |  |  | StyleResolver::StyleResolver(Document& document) | 
					
						
							|  |  |  |     : m_document(document) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | StyleResolver::~StyleResolver() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-05 09:01:12 +02:00
										 |  |  | static StyleSheet& default_stylesheet() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static StyleSheet* sheet; | 
					
						
							|  |  |  |     if (!sheet) { | 
					
						
							|  |  |  |         extern const char default_stylesheet_source[]; | 
					
						
							|  |  |  |         String css = default_stylesheet_source; | 
					
						
							| 
									
										
										
										
											2019-11-07 17:58:54 +01:00
										 |  |  |         sheet = parse_css(css).leak_ref(); | 
					
						
							| 
									
										
										
										
											2019-10-05 09:01:12 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return *sheet; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename Callback> | 
					
						
							|  |  |  | void StyleResolver::for_each_stylesheet(Callback callback) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     callback(default_stylesheet()); | 
					
						
							| 
									
										
										
										
											2020-06-04 16:06:32 +02:00
										 |  |  |     for (auto& sheet : document().style_sheets().sheets()) { | 
					
						
							| 
									
										
										
										
											2019-10-05 09:01:12 +02:00
										 |  |  |         callback(sheet); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  | NonnullRefPtrVector<StyleRule> StyleResolver::collect_matching_rules(const Element& element) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NonnullRefPtrVector<StyleRule> matching_rules; | 
					
						
							| 
									
										
										
										
											2019-10-05 09:01:12 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for_each_stylesheet([&](auto& sheet) { | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  |         for (auto& rule : sheet.rules()) { | 
					
						
							|  |  |  |             for (auto& selector : rule.selectors()) { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:33:58 +02:00
										 |  |  |                 if (SelectorEngine::matches(selector, element)) { | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  |                     matching_rules.append(rule); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-10-05 09:01:12 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:42:10 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef HTML_DEBUG
 | 
					
						
							| 
									
										
										
										
											2019-10-03 10:25:00 +02:00
										 |  |  |     dbgprintf("Rules matching Element{%p}\n", &element); | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  |     for (auto& rule : matching_rules) { | 
					
						
							|  |  |  |         dump_rule(rule); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-25 12:42:10 +03:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  |     return matching_rules; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | bool StyleResolver::is_inherited_property(CSS::PropertyID property_id) | 
					
						
							| 
									
										
										
										
											2019-10-04 22:52:49 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     static HashTable<CSS::PropertyID> inherited_properties; | 
					
						
							| 
									
										
										
										
											2019-10-04 22:52:49 +02:00
										 |  |  |     if (inherited_properties.is_empty()) { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |         inherited_properties.set(CSS::PropertyID::BorderCollapse); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::BorderSpacing); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::Color); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::FontFamily); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::FontSize); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::FontStyle); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::FontVariant); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::FontWeight); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::LetterSpacing); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::LineHeight); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::ListStyle); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::ListStyleImage); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::ListStylePosition); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::ListStyleType); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::TextAlign); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::TextIndent); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::TextTransform); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::Visibility); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::WhiteSpace); | 
					
						
							|  |  |  |         inherited_properties.set(CSS::PropertyID::WordSpacing); | 
					
						
							| 
									
										
										
										
											2019-10-04 22:52:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // FIXME: This property is not supposed to be inherited, but we currently
 | 
					
						
							|  |  |  |         //        rely on inheritance to propagate decorations into line boxes.
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |         inherited_properties.set(CSS::PropertyID::TextDecoration); | 
					
						
							| 
									
										
										
										
											2019-10-04 22:52:49 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     return inherited_properties.contains(property_id); | 
					
						
							| 
									
										
										
										
											2019-10-04 22:52:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  | static Vector<String> split_on_whitespace(const StringView& string) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (string.is_empty()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector<String> v; | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     size_t substart = 0; | 
					
						
							|  |  |  |     for (size_t i = 0; i < string.length(); ++i) { | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |         char ch = string.characters_without_null_termination()[i]; | 
					
						
							|  |  |  |         if (isspace(ch)) { | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |             size_t sublen = i - substart; | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |             if (sublen != 0) | 
					
						
							|  |  |  |                 v.append(string.substring_view(substart, sublen)); | 
					
						
							|  |  |  |             substart = i + 1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     size_t taillen = string.length() - substart; | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |     if (taillen != 0) | 
					
						
							|  |  |  |         v.append(string.substring_view(substart, taillen)); | 
					
						
							|  |  |  |     return v; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  | enum class Edge { | 
					
						
							|  |  |  |     Top, | 
					
						
							|  |  |  |     Right, | 
					
						
							|  |  |  |     Bottom, | 
					
						
							|  |  |  |     Left, | 
					
						
							|  |  |  |     All, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool contains(Edge a, Edge b) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return a == b || b == Edge::All; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void set_property_border_width(StyleProperties& style, const StyleValue& value, Edge edge) | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ASSERT(value.is_length()); | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |     if (contains(Edge::Top, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderTopWidth, value); | 
					
						
							|  |  |  |     if (contains(Edge::Right, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderRightWidth, value); | 
					
						
							|  |  |  |     if (contains(Edge::Bottom, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderBottomWidth, value); | 
					
						
							|  |  |  |     if (contains(Edge::Left, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderLeftWidth, value); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  | static inline void set_property_border_color(StyleProperties& style, const StyleValue& value, Edge edge) | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ASSERT(value.is_color()); | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |     if (contains(Edge::Top, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderTopColor, value); | 
					
						
							|  |  |  |     if (contains(Edge::Right, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderRightColor, value); | 
					
						
							|  |  |  |     if (contains(Edge::Bottom, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderBottomColor, value); | 
					
						
							|  |  |  |     if (contains(Edge::Left, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderLeftColor, value); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  | static inline void set_property_border_style(StyleProperties& style, const StyleValue& value, Edge edge) | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     ASSERT(value.is_string()); | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |     if (contains(Edge::Top, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderTopStyle, value); | 
					
						
							|  |  |  |     if (contains(Edge::Right, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderRightStyle, value); | 
					
						
							|  |  |  |     if (contains(Edge::Bottom, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderBottomStyle, value); | 
					
						
							|  |  |  |     if (contains(Edge::Left, edge)) | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderLeftStyle, value); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  | static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |     if (property_id == CSS::PropertyID::Border) { | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |         set_property_expanding_shorthands(style, CSS::PropertyID::BorderTop, value); | 
					
						
							|  |  |  |         set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value); | 
					
						
							|  |  |  |         set_property_expanding_shorthands(style, CSS::PropertyID::BorderBottom, value); | 
					
						
							|  |  |  |         set_property_expanding_shorthands(style, CSS::PropertyID::BorderLeft, value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (property_id == CSS::PropertyID::BorderTop | 
					
						
							|  |  |  |         || property_id == CSS::PropertyID::BorderRight | 
					
						
							|  |  |  |         || property_id == CSS::PropertyID::BorderBottom | 
					
						
							|  |  |  |         || property_id == CSS::PropertyID::BorderLeft) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Edge edge = Edge::All; | 
					
						
							|  |  |  |         switch (property_id) { | 
					
						
							|  |  |  |         case CSS::PropertyID::BorderTop: | 
					
						
							|  |  |  |             edge = Edge::Top; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CSS::PropertyID::BorderRight: | 
					
						
							|  |  |  |             edge = Edge::Right; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CSS::PropertyID::BorderBottom: | 
					
						
							|  |  |  |             edge = Edge::Bottom; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case CSS::PropertyID::BorderLeft: | 
					
						
							|  |  |  |             edge = Edge::Left; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |         auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |         if (value.is_length()) { | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |             set_property_border_width(style, value, edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value.is_color()) { | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |             set_property_border_color(style, value, edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value.is_string()) { | 
					
						
							|  |  |  |             auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (parts.size() == 1) { | 
					
						
							|  |  |  |                 if (auto value = parse_line_style(parts[0])) { | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |                     set_property_border_style(style, value.release_nonnull(), edge); | 
					
						
							|  |  |  |                     set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge); | 
					
						
							|  |  |  |                     set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |                     return; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             RefPtr<LengthStyleValue> line_width_value; | 
					
						
							|  |  |  |             RefPtr<ColorStyleValue> color_value; | 
					
						
							|  |  |  |             RefPtr<StringStyleValue> line_style_value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (auto& part : parts) { | 
					
						
							|  |  |  |                 if (auto value = parse_line_width(part)) { | 
					
						
							|  |  |  |                     if (line_width_value) | 
					
						
							|  |  |  |                         return; | 
					
						
							|  |  |  |                     line_width_value = move(value); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (auto value = parse_color(part)) { | 
					
						
							|  |  |  |                     if (color_value) | 
					
						
							|  |  |  |                         return; | 
					
						
							|  |  |  |                     color_value = move(value); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (auto value = parse_line_style(part)) { | 
					
						
							|  |  |  |                     if (line_style_value) | 
					
						
							|  |  |  |                         return; | 
					
						
							|  |  |  |                     line_style_value = move(value); | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (line_width_value) | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |                 set_property_border_width(style, line_width_value.release_nonnull(), edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |             if (color_value) | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |                 set_property_border_color(style, color_value.release_nonnull(), edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  |             if (line_style_value) | 
					
						
							| 
									
										
										
										
											2020-06-10 16:14:31 +02:00
										 |  |  |                 set_property_border_style(style, line_style_value.release_nonnull(), edge); | 
					
						
							| 
									
										
										
										
											2020-03-20 20:29:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 20:51:15 +01:00
										 |  |  |     if (property_id == CSS::PropertyID::BorderStyle) { | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderTopStyle, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderRightStyle, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderBottomStyle, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderLeftStyle, value); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (property_id == CSS::PropertyID::BorderWidth) { | 
					
						
							| 
									
										
										
										
											2020-06-10 16:42:58 +02:00
										 |  |  |         auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |         if (parts.size() == 2) { | 
					
						
							|  |  |  |             auto vertical_border_width = parse_css_value(parts[0]); | 
					
						
							|  |  |  |             auto horizonal_border_width = parse_css_value(parts[1]); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderTopWidth, vertical_border_width); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderRightWidth, horizonal_border_width); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderBottomWidth, vertical_border_width); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderLeftWidth, horizonal_border_width); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderTopWidth, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderRightWidth, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderBottomWidth, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BorderLeftWidth, value); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-11-27 20:51:15 +01:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (property_id == CSS::PropertyID::BorderColor) { | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderTopColor, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderRightColor, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderBottomColor, value); | 
					
						
							|  |  |  |         style.set_property(CSS::PropertyID::BorderLeftColor, value); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 11:58:05 +02:00
										 |  |  |     if (property_id == CSS::PropertyID::Background) { | 
					
						
							|  |  |  |         auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |         NonnullRefPtrVector<StyleValue> values; | 
					
						
							|  |  |  |         for (auto& part : parts) { | 
					
						
							|  |  |  |             values.append(parse_css_value(part)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (values[0].is_color()) | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::BackgroundColor, values[0]); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |     if (property_id == CSS::PropertyID::Margin) { | 
					
						
							|  |  |  |         if (value.is_length()) { | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::MarginTop, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::MarginRight, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::MarginBottom, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::MarginLeft, value); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value.is_string()) { | 
					
						
							|  |  |  |             auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |             if (parts.size() == 2) { | 
					
						
							|  |  |  |                 auto vertical = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto horizontal = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginTop, vertical); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginBottom, vertical); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginLeft, horizontal); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginRight, horizontal); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (parts.size() == 3) { | 
					
						
							|  |  |  |                 auto top = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto horizontal = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 auto bottom = parse_css_value(parts[2]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginTop, top); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginBottom, bottom); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginLeft, horizontal); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginRight, horizontal); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (parts.size() == 4) { | 
					
						
							|  |  |  |                 auto top = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto right = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 auto bottom = parse_css_value(parts[2]); | 
					
						
							|  |  |  |                 auto left = parse_css_value(parts[3]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginTop, top); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginBottom, bottom); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginLeft, left); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::MarginRight, right); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             dbg() << "Unsure what to do with CSS margin value '" << value.to_string() << "'"; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 12:25:22 +01:00
										 |  |  |     if (property_id == CSS::PropertyID::Padding) { | 
					
						
							|  |  |  |         if (value.is_length()) { | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::PaddingTop, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::PaddingRight, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::PaddingBottom, value); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::PaddingLeft, value); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (value.is_string()) { | 
					
						
							|  |  |  |             auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |             if (parts.size() == 2) { | 
					
						
							|  |  |  |                 auto vertical = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto horizontal = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingTop, vertical); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingBottom, vertical); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingLeft, horizontal); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingRight, horizontal); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (parts.size() == 3) { | 
					
						
							|  |  |  |                 auto top = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto horizontal = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 auto bottom = parse_css_value(parts[2]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingTop, top); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingBottom, bottom); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingLeft, horizontal); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingRight, horizontal); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (parts.size() == 4) { | 
					
						
							|  |  |  |                 auto top = parse_css_value(parts[0]); | 
					
						
							|  |  |  |                 auto right = parse_css_value(parts[1]); | 
					
						
							|  |  |  |                 auto bottom = parse_css_value(parts[2]); | 
					
						
							|  |  |  |                 auto left = parse_css_value(parts[3]); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingTop, top); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingBottom, bottom); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingLeft, left); | 
					
						
							|  |  |  |                 style.set_property(CSS::PropertyID::PaddingRight, right); | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             dbg() << "Unsure what to do with CSS padding value '" << value.to_string() << "'"; | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 12:04:15 +02:00
										 |  |  |     if (property_id == CSS::PropertyID::ListStyle) { | 
					
						
							|  |  |  |         auto parts = split_on_whitespace(value.to_string()); | 
					
						
							|  |  |  |         if (!parts.is_empty()) { | 
					
						
							|  |  |  |             auto value = parse_css_value(parts[0]); | 
					
						
							|  |  |  |             style.set_property(CSS::PropertyID::ListStyleType, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |     style.set_property(property_id, value); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-07 09:23:53 +02:00
										 |  |  | NonnullRefPtr<StyleProperties> StyleResolver::resolve_style(const Element& element, const StyleProperties* parent_style) const | 
					
						
							| 
									
										
										
										
											2019-06-27 17:47:59 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-07 09:23:53 +02:00
										 |  |  |     auto style = StyleProperties::create(); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:33:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-07 09:23:53 +02:00
										 |  |  |     if (parent_style) { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |         parent_style->for_each_property([&](auto property_id, auto& value) { | 
					
						
							|  |  |  |             if (is_inherited_property(property_id)) | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |                 set_property_expanding_shorthands(style, property_id, value); | 
					
						
							| 
									
										
										
										
											2019-09-25 12:33:28 +03:00
										 |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-07 09:23:53 +02:00
										 |  |  |     element.apply_presentational_hints(*style); | 
					
						
							| 
									
										
										
										
											2019-10-04 21:05:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 20:40:21 +02:00
										 |  |  |     auto matching_rules = collect_matching_rules(element); | 
					
						
							| 
									
										
										
										
											2020-06-10 16:41:30 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: We need to look at the specificity of the matching *selector*, not just the matched *rule*!
 | 
					
						
							|  |  |  |     // FIXME: It's really awkward that NonnullRefPtrVector cannot be quick_sort()'ed
 | 
					
						
							|  |  |  |     quick_sort(reinterpret_cast<Vector<NonnullRefPtr<StyleRule>>&>(matching_rules), [&](auto& a, auto& b) { | 
					
						
							|  |  |  |         return a->selectors().first().specificity() < b->selectors().first().specificity(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-28 21:17:34 +02:00
										 |  |  |     for (auto& rule : matching_rules) { | 
					
						
							| 
									
										
										
										
											2019-09-30 20:06:17 +02:00
										 |  |  |         for (auto& property : rule.declaration().properties()) { | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |             set_property_expanding_shorthands(style, property.property_id, property.value); | 
					
						
							| 
									
										
										
										
											2019-06-28 21:17:34 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-30 20:25:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-30 13:06:26 +02:00
										 |  |  |     auto style_attribute = element.attribute(HTML::AttributeNames::style); | 
					
						
							| 
									
										
										
										
											2019-09-30 20:25:33 +02:00
										 |  |  |     if (!style_attribute.is_null()) { | 
					
						
							|  |  |  |         if (auto declaration = parse_css_declaration(style_attribute)) { | 
					
						
							|  |  |  |             for (auto& property : declaration->properties()) { | 
					
						
							| 
									
										
										
										
											2019-11-18 11:49:19 +01:00
										 |  |  |                 set_property_expanding_shorthands(style, property.property_id, property.value); | 
					
						
							| 
									
										
										
										
											2019-09-30 20:25:33 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-07 09:23:53 +02:00
										 |  |  |     return style; | 
					
						
							| 
									
										
										
										
											2019-06-27 17:47:59 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |