Serenity Operating System
at master 215 lines 6.4 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org> 4 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <AK/NonnullOwnPtr.h> 10#include <AK/Variant.h> 11#include <LibGfx/Font/Font.h> 12#include <LibGfx/Rect.h> 13#include <LibWeb/CSS/Length.h> 14#include <LibWeb/CSS/Percentage.h> 15#include <LibWeb/DOM/Document.h> 16#include <LibWeb/HTML/BrowsingContext.h> 17#include <LibWeb/HTML/HTMLHtmlElement.h> 18 19namespace Web::CSS { 20 21Length::Length(int value, Type type) 22 : m_type(type) 23 , m_value(value) 24{ 25} 26Length::Length(float value, Type type) 27 : m_type(type) 28 , m_value(value) 29{ 30} 31Length::~Length() = default; 32 33Length Length::make_auto() 34{ 35 return Length(0, Type::Auto); 36} 37 38Length Length::make_px(CSSPixels value) 39{ 40 return Length(value.value(), Type::Px); 41} 42 43Length Length::make_calculated(NonnullRefPtr<CalculatedStyleValue> calculated_style_value) 44{ 45 Length length { 0, Type::Calculated }; 46 length.m_calculated_style = move(calculated_style_value); 47 return length; 48} 49 50Length Length::percentage_of(Percentage const& percentage) const 51{ 52 VERIFY(!is_calculated()); 53 54 if (is_auto()) { 55 dbgln("Attempting to get percentage of an auto length, this seems wrong? But for now we just return the original length."); 56 return *this; 57 } 58 59 return Length { percentage.as_fraction() * raw_value(), m_type }; 60} 61 62Length Length::resolved(Layout::Node const& layout_node) const 63{ 64 if (is_calculated()) 65 return m_calculated_style->resolve_length(layout_node).release_value(); 66 if (is_relative()) 67 return make_px(to_px(layout_node)); 68 if (!isfinite(m_value)) 69 return make_auto(); 70 return *this; 71} 72 73CSSPixels Length::relative_length_to_px(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const 74{ 75 switch (m_type) { 76 case Type::Ex: 77 return m_value * font_metrics.x_height; 78 case Type::Em: 79 return m_value * font_size; 80 case Type::Ch: 81 // FIXME: Use layout_node.font().pixel_size() when writing-mode is not horizontal-tb (it has to be implemented first) 82 return m_value * (font_metrics.advance_of_ascii_zero + font_metrics.glyph_spacing); 83 case Type::Rem: 84 return m_value * root_font_size; 85 case Type::Vw: 86 return viewport_rect.width() * (m_value / 100); 87 case Type::Vh: 88 return viewport_rect.height() * (m_value / 100); 89 case Type::Vmin: 90 return min(viewport_rect.width(), viewport_rect.height()) * (m_value / 100); 91 case Type::Vmax: 92 return max(viewport_rect.width(), viewport_rect.height()) * (m_value / 100); 93 default: 94 VERIFY_NOT_REACHED(); 95 } 96} 97 98CSSPixels Length::to_px(Layout::Node const& layout_node) const 99{ 100 if (is_calculated()) 101 return m_calculated_style->resolve_length(layout_node)->to_px(layout_node); 102 103 if (is_absolute()) 104 return absolute_length_to_px(); 105 106 if (!layout_node.document().browsing_context()) 107 return 0; 108 auto const& viewport_rect = layout_node.document().browsing_context()->viewport_rect(); 109 auto* root_element = layout_node.document().document_element(); 110 if (!root_element || !root_element->layout_node()) 111 return 0; 112 return to_px(viewport_rect, layout_node.font().pixel_metrics(), layout_node.computed_values().font_size(), root_element->layout_node()->computed_values().font_size()); 113} 114 115ErrorOr<String> Length::to_string() const 116{ 117 if (is_calculated()) 118 return m_calculated_style->to_string(); 119 if (is_auto()) 120 return "auto"_string; 121 return String::formatted("{}{}", m_value, unit_name()); 122} 123 124char const* Length::unit_name() const 125{ 126 switch (m_type) { 127 case Type::Cm: 128 return "cm"; 129 case Type::In: 130 return "in"; 131 case Type::Px: 132 return "px"; 133 case Type::Pt: 134 return "pt"; 135 case Type::Mm: 136 return "mm"; 137 case Type::Q: 138 return "Q"; 139 case Type::Pc: 140 return "pc"; 141 case Type::Ex: 142 return "ex"; 143 case Type::Em: 144 return "em"; 145 case Type::Ch: 146 return "ch"; 147 case Type::Rem: 148 return "rem"; 149 case Type::Auto: 150 return "auto"; 151 case Type::Vh: 152 return "vh"; 153 case Type::Vw: 154 return "vw"; 155 case Type::Vmax: 156 return "vmax"; 157 case Type::Vmin: 158 return "vmin"; 159 case Type::Calculated: 160 return "calculated"; 161 } 162 VERIFY_NOT_REACHED(); 163} 164 165Optional<Length::Type> Length::unit_from_name(StringView name) 166{ 167 if (name.equals_ignoring_ascii_case("px"sv)) { 168 return Length::Type::Px; 169 } else if (name.equals_ignoring_ascii_case("pt"sv)) { 170 return Length::Type::Pt; 171 } else if (name.equals_ignoring_ascii_case("pc"sv)) { 172 return Length::Type::Pc; 173 } else if (name.equals_ignoring_ascii_case("mm"sv)) { 174 return Length::Type::Mm; 175 } else if (name.equals_ignoring_ascii_case("rem"sv)) { 176 return Length::Type::Rem; 177 } else if (name.equals_ignoring_ascii_case("em"sv)) { 178 return Length::Type::Em; 179 } else if (name.equals_ignoring_ascii_case("ex"sv)) { 180 return Length::Type::Ex; 181 } else if (name.equals_ignoring_ascii_case("ch"sv)) { 182 return Length::Type::Ch; 183 } else if (name.equals_ignoring_ascii_case("vw"sv)) { 184 return Length::Type::Vw; 185 } else if (name.equals_ignoring_ascii_case("vh"sv)) { 186 return Length::Type::Vh; 187 } else if (name.equals_ignoring_ascii_case("vmax"sv)) { 188 return Length::Type::Vmax; 189 } else if (name.equals_ignoring_ascii_case("vmin"sv)) { 190 return Length::Type::Vmin; 191 } else if (name.equals_ignoring_ascii_case("cm"sv)) { 192 return Length::Type::Cm; 193 } else if (name.equals_ignoring_ascii_case("in"sv)) { 194 return Length::Type::In; 195 } else if (name.equals_ignoring_ascii_case("Q"sv)) { 196 return Length::Type::Q; 197 } 198 199 return {}; 200} 201 202NonnullRefPtr<CalculatedStyleValue> Length::calculated_style_value() const 203{ 204 VERIFY(!m_calculated_style.is_null()); 205 return *m_calculated_style; 206} 207 208bool Length::operator==(Length const& other) const 209{ 210 if (is_calculated()) 211 return m_calculated_style == other.m_calculated_style; 212 return m_type == other.m_type && m_value == other.m_value; 213} 214 215}