| 
									
										
										
										
											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-02-06 15:04:03 +01:00
										 |  |  | #include <LibCore/DirIterator.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:32:51 +01:00
										 |  |  | #include <LibWeb/CSS/StyleProperties.h>
 | 
					
						
							|  |  |  | #include <LibWeb/FontCache.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  | #include <ctype.h>
 | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | namespace Web { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-05 16:54:38 +01:00
										 |  |  | StyleProperties::StyleProperties() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | StyleProperties::StyleProperties(const StyleProperties& other) | 
					
						
							|  |  |  |     : m_property_values(*new HashMap(other.m_property_values)) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (other.m_font) { | 
					
						
							|  |  |  |         m_font = other.m_font->clone(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         m_font = nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NonnullRefPtr<StyleProperties> StyleProperties::clone() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return adopt(*new StyleProperties(*this)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr<StyleValue> value) | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     m_property_values.set((unsigned)id, move(value)); | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | Optional<NonnullRefPtr<StyleValue>> StyleProperties::property(CSS::PropertyID id) const | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     auto it = m_property_values.find((unsigned)id); | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  |     if (it == m_property_values.end()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     return it->value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | Length StyleProperties::length_or_fallback(CSS::PropertyID id, const Length& fallback) const | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     auto value = property(id); | 
					
						
							| 
									
										
										
										
											2019-09-21 15:32:17 +03:00
										 |  |  |     if (!value.has_value()) | 
					
						
							|  |  |  |         return fallback; | 
					
						
							|  |  |  |     return value.value()->to_length(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-25 11:55:04 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | String StyleProperties::string_or_fallback(CSS::PropertyID id, const StringView& fallback) const | 
					
						
							| 
									
										
										
										
											2019-09-25 11:55:04 +03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     auto value = property(id); | 
					
						
							| 
									
										
										
										
											2019-09-25 11:55:04 +03:00
										 |  |  |     if (!value.has_value()) | 
					
						
							|  |  |  |         return fallback; | 
					
						
							|  |  |  |     return value.value()->to_string(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-28 22:18:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  | Color StyleProperties::color_or_fallback(CSS::PropertyID id, const Document& document, Color fallback) const | 
					
						
							| 
									
										
										
										
											2019-09-28 22:18:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     auto value = property(id); | 
					
						
							| 
									
										
										
										
											2019-09-28 22:18:19 +02:00
										 |  |  |     if (!value.has_value()) | 
					
						
							|  |  |  |         return fallback; | 
					
						
							| 
									
										
										
										
											2019-10-06 10:25:08 +02:00
										 |  |  |     return value.value()->to_color(document); | 
					
						
							| 
									
										
										
										
											2019-09-28 22:18:19 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | void StyleProperties::load_font() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-10-08 15:34:19 +02:00
										 |  |  |     auto font_family = string_or_fallback(CSS::PropertyID::FontFamily, "Katica"); | 
					
						
							|  |  |  |     auto font_weight = string_or_fallback(CSS::PropertyID::FontWeight, "normal"); | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-18 23:02:10 +02:00
										 |  |  |     if (auto cached_font = FontCache::the().get({ font_family, font_weight })) { | 
					
						
							|  |  |  |         m_font = cached_font; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  |     String weight; | 
					
						
							|  |  |  |     if (font_weight == "lighter") | 
					
						
							|  |  |  |         weight = "Thin"; | 
					
						
							|  |  |  |     else if (font_weight == "normal") | 
					
						
							|  |  |  |         weight = ""; | 
					
						
							|  |  |  |     else if (font_weight == "bold") | 
					
						
							|  |  |  |         weight = "Bold"; | 
					
						
							| 
									
										
										
										
											2019-10-17 23:54:00 +02:00
										 |  |  |     else { | 
					
						
							|  |  |  |         dbg() << "Unknown font-weight: " << font_weight; | 
					
						
							|  |  |  |         weight = ""; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto look_for_file = [](const StringView& expected_name) -> String { | 
					
						
							|  |  |  |         // TODO: handle font sizes properly?
 | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  |         Core::DirIterator it { "/res/fonts/", Core::DirIterator::Flags::SkipDots }; | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  |         while (it.has_next()) { | 
					
						
							|  |  |  |             String name = it.next_path(); | 
					
						
							| 
									
										
										
										
											2020-03-11 18:22:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (!name.ends_with(".font")) | 
					
						
							|  |  |  |                 continue; | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  |             if (!name.starts_with(expected_name)) | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Check that a numeric size immediately
 | 
					
						
							|  |  |  |             // follows the font name. This prevents,
 | 
					
						
							|  |  |  |             // for example, matching KaticaBold when
 | 
					
						
							|  |  |  |             // the regular Katica is requested.
 | 
					
						
							|  |  |  |             if (!isdigit(name[expected_name.length()])) | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return name; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     String file_name = look_for_file(String::format("%s%s", font_family.characters(), weight.characters())); | 
					
						
							|  |  |  |     if (file_name.is_null() && weight == "") | 
					
						
							|  |  |  |         file_name = look_for_file(String::format("%sRegular", font_family.characters())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (file_name.is_null()) { | 
					
						
							|  |  |  |         dbg() << "Failed to find a font for family " << font_family << " weight " << font_weight; | 
					
						
							| 
									
										
										
										
											2019-11-19 18:23:58 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (font_weight == "bold") | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  |             m_font = Gfx::Font::default_bold_font(); | 
					
						
							| 
									
										
										
										
											2019-11-19 18:23:58 +01:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  |             m_font = Gfx::Font::default_font(); | 
					
						
							| 
									
										
										
										
											2019-10-06 14:57:49 +02:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef HTML_DEBUG
 | 
					
						
							|  |  |  |     dbg() << "Found font " << file_name << " for family " << font_family << " weight " << font_weight; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  |     m_font = Gfx::Font::load_from_file(String::format("/res/fonts/%s", file_name.characters())); | 
					
						
							| 
									
										
										
										
											2019-10-18 23:02:10 +02:00
										 |  |  |     FontCache::the().set({ font_family, font_weight }, *m_font); | 
					
						
							| 
									
										
										
										
											2019-10-06 11:23:58 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-12 13:42:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 16:25:38 +01:00
										 |  |  | float StyleProperties::line_height() const | 
					
						
							| 
									
										
										
										
											2019-10-12 13:42:58 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-11-18 16:54:20 +01:00
										 |  |  |     auto line_height_length = length_or_fallback(CSS::PropertyID::LineHeight, {}); | 
					
						
							|  |  |  |     if (line_height_length.is_absolute()) | 
					
						
							|  |  |  |         return (float)font().glyph_height() * line_height_length.to_px(); | 
					
						
							| 
									
										
										
										
											2019-11-18 16:25:38 +01:00
										 |  |  |     return (float)font().glyph_height() * 1.4f; | 
					
						
							| 
									
										
										
										
											2019-10-12 13:42:58 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-14 18:32:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-23 17:29:15 +01:00
										 |  |  | CSS::Position StyleProperties::position() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (property(CSS::PropertyID::Position).has_value()) { | 
					
						
							|  |  |  |         String position_string = string_or_fallback(CSS::PropertyID::Position, "static"); | 
					
						
							|  |  |  |         if (position_string == "relative") | 
					
						
							|  |  |  |             return CSS::Position::Relative; | 
					
						
							|  |  |  |         if (position_string == "absolute") | 
					
						
							|  |  |  |             return CSS::Position::Absolute; | 
					
						
							|  |  |  |         if (position_string == "sticky") | 
					
						
							|  |  |  |             return CSS::Position::Sticky; | 
					
						
							|  |  |  |         if (position_string == "fixed") | 
					
						
							|  |  |  |             return CSS::Position::Fixed; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return CSS::Position::Static; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-14 18:32:02 +02:00
										 |  |  | bool StyleProperties::operator==(const StyleProperties& other) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_property_values.size() != other.m_property_values.size()) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto& it : m_property_values) { | 
					
						
							|  |  |  |         auto jt = other.m_property_values.find(it.key); | 
					
						
							|  |  |  |         if (jt == other.m_property_values.end()) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         auto& my_value = *it.value; | 
					
						
							|  |  |  |         auto& other_value = *jt->value; | 
					
						
							|  |  |  |         if (my_value.type() != other_value.type()) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         if (my_value.to_string() != other_value.to_string()) | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-07 10:27:02 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |