Length.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /*
  2. * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. * Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/NonnullOwnPtr.h>
  9. #include <AK/Variant.h>
  10. #include <LibGfx/Font/Font.h>
  11. #include <LibGfx/Rect.h>
  12. #include <LibWeb/CSS/Length.h>
  13. #include <LibWeb/CSS/Percentage.h>
  14. #include <LibWeb/DOM/Document.h>
  15. #include <LibWeb/HTML/BrowsingContext.h>
  16. #include <LibWeb/HTML/HTMLHtmlElement.h>
  17. #include <LibWeb/HTML/Window.h>
  18. #include <LibWeb/Layout/Node.h>
  19. namespace Web::CSS {
  20. Length::FontMetrics::FontMetrics(CSSPixels font_size, Gfx::FontPixelMetrics const& pixel_metrics)
  21. : font_size(font_size)
  22. , x_height(pixel_metrics.x_height)
  23. // FIXME: This is only approximately the cap height. The spec suggests measuring the "O" glyph:
  24. // https://www.w3.org/TR/css-values-4/#cap
  25. , cap_height(pixel_metrics.ascent)
  26. , zero_advance(pixel_metrics.advance_of_ascii_zero)
  27. , line_height(round(pixel_metrics.line_spacing()))
  28. {
  29. }
  30. Length::Length(double value, Type type)
  31. : m_type(type)
  32. , m_value(value)
  33. {
  34. }
  35. Length::~Length() = default;
  36. Length Length::make_auto()
  37. {
  38. return Length(0, Type::Auto);
  39. }
  40. Length Length::make_px(CSSPixels value)
  41. {
  42. return Length(value.to_double(), Type::Px);
  43. }
  44. Length Length::percentage_of(Percentage const& percentage) const
  45. {
  46. if (is_auto()) {
  47. dbgln("Attempting to get percentage of an auto length, this seems wrong? But for now we just return the original length.");
  48. return *this;
  49. }
  50. return Length { percentage.as_fraction() * raw_value(), m_type };
  51. }
  52. CSSPixels Length::font_relative_length_to_px(Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const
  53. {
  54. switch (m_type) {
  55. case Type::Em:
  56. return CSSPixels::nearest_value_for(m_value * font_metrics.font_size.to_double());
  57. case Type::Rem:
  58. return CSSPixels::nearest_value_for(m_value * root_font_metrics.font_size.to_double());
  59. case Type::Ex:
  60. return CSSPixels::nearest_value_for(m_value * font_metrics.x_height.to_double());
  61. case Type::Rex:
  62. return CSSPixels::nearest_value_for(m_value * root_font_metrics.x_height.to_double());
  63. case Type::Cap:
  64. return CSSPixels::nearest_value_for(m_value * font_metrics.cap_height.to_double());
  65. case Type::Rcap:
  66. return CSSPixels::nearest_value_for(m_value * root_font_metrics.cap_height.to_double());
  67. case Type::Ch:
  68. return CSSPixels::nearest_value_for(m_value * font_metrics.zero_advance.to_double());
  69. case Type::Rch:
  70. return CSSPixels::nearest_value_for(m_value * root_font_metrics.zero_advance.to_double());
  71. case Type::Ic:
  72. // FIXME: Use the "advance measure of the “水” (CJK water ideograph, U+6C34) glyph"
  73. return CSSPixels::nearest_value_for(m_value * font_metrics.font_size.to_double());
  74. case Type::Ric:
  75. // FIXME: Use the "advance measure of the “水” (CJK water ideograph, U+6C34) glyph"
  76. return CSSPixels::nearest_value_for(m_value * root_font_metrics.font_size.to_double());
  77. case Type::Lh:
  78. return CSSPixels::nearest_value_for(m_value * font_metrics.line_height.to_double());
  79. case Type::Rlh:
  80. return CSSPixels::nearest_value_for(m_value * root_font_metrics.line_height.to_double());
  81. default:
  82. VERIFY_NOT_REACHED();
  83. }
  84. }
  85. CSSPixels Length::viewport_relative_length_to_px(CSSPixelRect const& viewport_rect) const
  86. {
  87. switch (m_type) {
  88. case Type::Vw:
  89. case Type::Svw:
  90. case Type::Lvw:
  91. case Type::Dvw:
  92. return viewport_rect.width() * (CSSPixels::nearest_value_for(m_value) / 100);
  93. case Type::Vh:
  94. case Type::Svh:
  95. case Type::Lvh:
  96. case Type::Dvh:
  97. return viewport_rect.height() * (CSSPixels::nearest_value_for(m_value) / 100);
  98. case Type::Vi:
  99. case Type::Svi:
  100. case Type::Lvi:
  101. case Type::Dvi:
  102. // FIXME: Select the width or height based on which is the inline axis.
  103. return viewport_rect.width() * (CSSPixels::nearest_value_for(m_value) / 100);
  104. case Type::Vb:
  105. case Type::Svb:
  106. case Type::Lvb:
  107. case Type::Dvb:
  108. // FIXME: Select the width or height based on which is the block axis.
  109. return viewport_rect.height() * (CSSPixels::nearest_value_for(m_value) / 100);
  110. case Type::Vmin:
  111. case Type::Svmin:
  112. case Type::Lvmin:
  113. case Type::Dvmin:
  114. return min(viewport_rect.width(), viewport_rect.height()) * (CSSPixels::nearest_value_for(m_value) / 100);
  115. case Type::Vmax:
  116. case Type::Svmax:
  117. case Type::Lvmax:
  118. case Type::Dvmax:
  119. return max(viewport_rect.width(), viewport_rect.height()) * (CSSPixels::nearest_value_for(m_value) / 100);
  120. default:
  121. VERIFY_NOT_REACHED();
  122. }
  123. }
  124. Length::ResolutionContext Length::ResolutionContext::for_window(HTML::Window const& window)
  125. {
  126. auto const& initial_font = window.associated_document().style_computer().initial_font();
  127. Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
  128. Length::FontMetrics font_metrics { CSSPixels { initial_font.pixel_size() }, initial_font_metrics };
  129. return Length::ResolutionContext {
  130. .viewport_rect = window.page().web_exposed_screen_area(),
  131. .font_metrics = font_metrics,
  132. .root_font_metrics = font_metrics,
  133. };
  134. }
  135. Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Node const& node)
  136. {
  137. auto const* root_element = node.document().document_element();
  138. VERIFY(root_element);
  139. VERIFY(root_element->layout_node());
  140. return Length::ResolutionContext {
  141. .viewport_rect = node.navigable()->viewport_rect(),
  142. .font_metrics = { node.computed_values().font_size(), node.first_available_font().pixel_metrics() },
  143. .root_font_metrics = { root_element->layout_node()->computed_values().font_size(), root_element->layout_node()->first_available_font().pixel_metrics() },
  144. };
  145. }
  146. CSSPixels Length::to_px(ResolutionContext const& context) const
  147. {
  148. return to_px(context.viewport_rect, context.font_metrics, context.root_font_metrics);
  149. }
  150. CSSPixels Length::to_px_slow_case(Layout::Node const& layout_node) const
  151. {
  152. if (is_auto()) {
  153. // FIXME: We really, really shouldn't end up here, but we do, and so frequently that
  154. // adding a dbgln() here outputs a couple hundred lines loading `welcome.html`.
  155. return 0;
  156. }
  157. if (!layout_node.document().browsing_context())
  158. return 0;
  159. if (is_font_relative()) {
  160. auto* root_element = layout_node.document().document_element();
  161. if (!root_element || !root_element->layout_node())
  162. return 0;
  163. FontMetrics font_metrics {
  164. layout_node.computed_values().font_size(),
  165. layout_node.first_available_font().pixel_metrics(),
  166. };
  167. FontMetrics root_font_metrics {
  168. root_element->layout_node()->computed_values().font_size(),
  169. root_element->layout_node()->first_available_font().pixel_metrics(),
  170. };
  171. return font_relative_length_to_px(font_metrics, root_font_metrics);
  172. }
  173. VERIFY(is_viewport_relative());
  174. auto const& viewport_rect = layout_node.document().viewport_rect();
  175. return viewport_relative_length_to_px(viewport_rect);
  176. }
  177. String Length::to_string() const
  178. {
  179. if (is_auto())
  180. return "auto"_string;
  181. return MUST(String::formatted("{:.5}{}", m_value, unit_name()));
  182. }
  183. StringView Length::unit_name() const
  184. {
  185. switch (m_type) {
  186. case Type::Em:
  187. return "em"sv;
  188. case Type::Rem:
  189. return "rem"sv;
  190. case Type::Ex:
  191. return "ex"sv;
  192. case Type::Rex:
  193. return "rex"sv;
  194. case Type::Cap:
  195. return "cap"sv;
  196. case Type::Rcap:
  197. return "rcap"sv;
  198. case Type::Ch:
  199. return "ch"sv;
  200. case Type::Rch:
  201. return "rch"sv;
  202. case Type::Ic:
  203. return "ic"sv;
  204. case Type::Ric:
  205. return "ric"sv;
  206. case Type::Lh:
  207. return "lh"sv;
  208. case Type::Rlh:
  209. return "rlh"sv;
  210. case Type::Vw:
  211. return "vw"sv;
  212. case Type::Svw:
  213. return "svw"sv;
  214. case Type::Lvw:
  215. return "lvw"sv;
  216. case Type::Dvw:
  217. return "dvw"sv;
  218. case Type::Vh:
  219. return "vh"sv;
  220. case Type::Svh:
  221. return "svh"sv;
  222. case Type::Lvh:
  223. return "lvh"sv;
  224. case Type::Dvh:
  225. return "dvh"sv;
  226. case Type::Vi:
  227. return "vi"sv;
  228. case Type::Svi:
  229. return "svi"sv;
  230. case Type::Lvi:
  231. return "lvi"sv;
  232. case Type::Dvi:
  233. return "dvi"sv;
  234. case Type::Vb:
  235. return "vb"sv;
  236. case Type::Svb:
  237. return "svb"sv;
  238. case Type::Lvb:
  239. return "lvb"sv;
  240. case Type::Dvb:
  241. return "dvb"sv;
  242. case Type::Vmin:
  243. return "vmin"sv;
  244. case Type::Svmin:
  245. return "svmin"sv;
  246. case Type::Lvmin:
  247. return "lvmin"sv;
  248. case Type::Dvmin:
  249. return "dvmin"sv;
  250. case Type::Vmax:
  251. return "vmax"sv;
  252. case Type::Svmax:
  253. return "svmax"sv;
  254. case Type::Lvmax:
  255. return "lvmax"sv;
  256. case Type::Dvmax:
  257. return "dvmax"sv;
  258. case Type::Cm:
  259. return "cm"sv;
  260. case Type::Mm:
  261. return "mm"sv;
  262. case Type::Q:
  263. return "Q"sv;
  264. case Type::In:
  265. return "in"sv;
  266. case Type::Pt:
  267. return "pt"sv;
  268. case Type::Pc:
  269. return "pc"sv;
  270. case Type::Px:
  271. return "px"sv;
  272. case Type::Auto:
  273. return "auto"sv;
  274. }
  275. VERIFY_NOT_REACHED();
  276. }
  277. Optional<Length::Type> Length::unit_from_name(StringView name)
  278. {
  279. if (name.equals_ignoring_ascii_case("em"sv)) {
  280. return Length::Type::Em;
  281. } else if (name.equals_ignoring_ascii_case("rem"sv)) {
  282. return Length::Type::Rem;
  283. } else if (name.equals_ignoring_ascii_case("ex"sv)) {
  284. return Length::Type::Ex;
  285. } else if (name.equals_ignoring_ascii_case("rex"sv)) {
  286. return Length::Type::Rex;
  287. } else if (name.equals_ignoring_ascii_case("cap"sv)) {
  288. return Length::Type::Cap;
  289. } else if (name.equals_ignoring_ascii_case("rcap"sv)) {
  290. return Length::Type::Rcap;
  291. } else if (name.equals_ignoring_ascii_case("ch"sv)) {
  292. return Length::Type::Ch;
  293. } else if (name.equals_ignoring_ascii_case("rch"sv)) {
  294. return Length::Type::Rch;
  295. } else if (name.equals_ignoring_ascii_case("ic"sv)) {
  296. return Length::Type::Ic;
  297. } else if (name.equals_ignoring_ascii_case("ric"sv)) {
  298. return Length::Type::Ric;
  299. } else if (name.equals_ignoring_ascii_case("lh"sv)) {
  300. return Length::Type::Lh;
  301. } else if (name.equals_ignoring_ascii_case("rlh"sv)) {
  302. return Length::Type::Rlh;
  303. } else if (name.equals_ignoring_ascii_case("vw"sv)) {
  304. return Length::Type::Vw;
  305. } else if (name.equals_ignoring_ascii_case("svw"sv)) {
  306. return Length::Type::Svw;
  307. } else if (name.equals_ignoring_ascii_case("lvw"sv)) {
  308. return Length::Type::Lvw;
  309. } else if (name.equals_ignoring_ascii_case("dvw"sv)) {
  310. return Length::Type::Dvw;
  311. } else if (name.equals_ignoring_ascii_case("vh"sv)) {
  312. return Length::Type::Vh;
  313. } else if (name.equals_ignoring_ascii_case("svh"sv)) {
  314. return Length::Type::Svh;
  315. } else if (name.equals_ignoring_ascii_case("lvh"sv)) {
  316. return Length::Type::Lvh;
  317. } else if (name.equals_ignoring_ascii_case("dvh"sv)) {
  318. return Length::Type::Dvh;
  319. } else if (name.equals_ignoring_ascii_case("vi"sv)) {
  320. return Length::Type::Vi;
  321. } else if (name.equals_ignoring_ascii_case("svi"sv)) {
  322. return Length::Type::Svi;
  323. } else if (name.equals_ignoring_ascii_case("lvi"sv)) {
  324. return Length::Type::Lvi;
  325. } else if (name.equals_ignoring_ascii_case("dvi"sv)) {
  326. return Length::Type::Dvi;
  327. } else if (name.equals_ignoring_ascii_case("vb"sv)) {
  328. return Length::Type::Vb;
  329. } else if (name.equals_ignoring_ascii_case("svb"sv)) {
  330. return Length::Type::Svb;
  331. } else if (name.equals_ignoring_ascii_case("lvb"sv)) {
  332. return Length::Type::Lvb;
  333. } else if (name.equals_ignoring_ascii_case("dvb"sv)) {
  334. return Length::Type::Dvb;
  335. } else if (name.equals_ignoring_ascii_case("vmin"sv)) {
  336. return Length::Type::Vmin;
  337. } else if (name.equals_ignoring_ascii_case("svmin"sv)) {
  338. return Length::Type::Svmin;
  339. } else if (name.equals_ignoring_ascii_case("lvmin"sv)) {
  340. return Length::Type::Lvmin;
  341. } else if (name.equals_ignoring_ascii_case("dvmin"sv)) {
  342. return Length::Type::Dvmin;
  343. } else if (name.equals_ignoring_ascii_case("vmax"sv)) {
  344. return Length::Type::Vmax;
  345. } else if (name.equals_ignoring_ascii_case("svmax"sv)) {
  346. return Length::Type::Svmax;
  347. } else if (name.equals_ignoring_ascii_case("lvmax"sv)) {
  348. return Length::Type::Lvmax;
  349. } else if (name.equals_ignoring_ascii_case("dvmax"sv)) {
  350. return Length::Type::Dvmax;
  351. } else if (name.equals_ignoring_ascii_case("cm"sv)) {
  352. return Length::Type::Cm;
  353. } else if (name.equals_ignoring_ascii_case("mm"sv)) {
  354. return Length::Type::Mm;
  355. } else if (name.equals_ignoring_ascii_case("Q"sv)) {
  356. return Length::Type::Q;
  357. } else if (name.equals_ignoring_ascii_case("in"sv)) {
  358. return Length::Type::In;
  359. } else if (name.equals_ignoring_ascii_case("pt"sv)) {
  360. return Length::Type::Pt;
  361. } else if (name.equals_ignoring_ascii_case("pc"sv)) {
  362. return Length::Type::Pc;
  363. } else if (name.equals_ignoring_ascii_case("px"sv)) {
  364. return Length::Type::Px;
  365. }
  366. return {};
  367. }
  368. Optional<Length> Length::absolutize(CSSPixelRect const& viewport_rect, FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const
  369. {
  370. if (is_px())
  371. return {};
  372. if (is_absolute() || is_relative()) {
  373. auto px = to_px(viewport_rect, font_metrics, root_font_metrics);
  374. return CSS::Length::make_px(px);
  375. }
  376. return {};
  377. }
  378. Length Length::absolutized(CSSPixelRect const& viewport_rect, FontMetrics const& font_metrics, FontMetrics const& root_font_metrics) const
  379. {
  380. return absolutize(viewport_rect, font_metrics, root_font_metrics).value_or(*this);
  381. }
  382. Length Length::resolve_calculated(NonnullRefPtr<CSSMathValue> const& calculated, Layout::Node const& layout_node, Length const& reference_value)
  383. {
  384. return calculated->resolve_length_percentage(layout_node, reference_value).value();
  385. }
  386. Length Length::resolve_calculated(NonnullRefPtr<CSSMathValue> const& calculated, Layout::Node const& layout_node, CSSPixels reference_value)
  387. {
  388. return calculated->resolve_length_percentage(layout_node, reference_value).value();
  389. }
  390. }