FlexFormattingContext.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "InlineFormattingContext.h"
  8. #include <AK/Function.h>
  9. #include <AK/StdLibExtras.h>
  10. #include <LibWeb/Layout/BlockContainer.h>
  11. #include <LibWeb/Layout/BlockFormattingContext.h>
  12. #include <LibWeb/Layout/Box.h>
  13. #include <LibWeb/Layout/FlexFormattingContext.h>
  14. #include <LibWeb/Layout/InitialContainingBlock.h>
  15. #include <LibWeb/Layout/TextNode.h>
  16. namespace Web::Layout {
  17. static float get_pixel_size(Box const& box, CSS::Length const& length)
  18. {
  19. return length.resolved(CSS::Length::make_px(0), box, box.containing_block()->width()).to_px(box);
  20. }
  21. FlexFormattingContext::FlexFormattingContext(Box& flex_container, FormattingContext* parent)
  22. : FormattingContext(flex_container, parent)
  23. , m_flex_direction(flex_container.computed_values().flex_direction())
  24. {
  25. }
  26. FlexFormattingContext::~FlexFormattingContext()
  27. {
  28. }
  29. struct DirectionAgnosticMargins {
  30. float main_before { 0 };
  31. float main_after { 0 };
  32. float cross_before { 0 };
  33. float cross_after { 0 };
  34. };
  35. struct FlexItem {
  36. Box& box;
  37. float flex_base_size { 0 };
  38. float hypothetical_main_size { 0 };
  39. float hypothetical_cross_size { 0 };
  40. float hypothetical_cross_size_with_margins() { return hypothetical_cross_size + margins.cross_before + margins.cross_after; }
  41. float target_main_size { 0 };
  42. bool frozen { false };
  43. Optional<float> flex_factor {};
  44. float scaled_flex_shrink_factor { 0 };
  45. float max_content_flex_fraction { 0 };
  46. float main_size { 0 };
  47. float cross_size { 0 };
  48. float main_offset { 0 };
  49. float cross_offset { 0 };
  50. DirectionAgnosticMargins margins {};
  51. bool is_min_violation { false };
  52. bool is_max_violation { false };
  53. };
  54. struct FlexLine {
  55. Vector<FlexItem*> items;
  56. float cross_size { 0 };
  57. };
  58. void FlexFormattingContext::run(Box& flex_container, LayoutMode)
  59. {
  60. // This implements https://www.w3.org/TR/css-flexbox-1/#layout-algorithm
  61. // FIXME: Implement reverse and ordering.
  62. bool is_row = is_row_layout();
  63. auto calculate_hypothetical_cross_size = [&is_row, this](Box& box) {
  64. bool cross_constrained = false;
  65. if (is_row) {
  66. if (!box.computed_values().height().is_undefined_or_auto() || !box.computed_values().min_height().is_undefined_or_auto()) {
  67. cross_constrained = true;
  68. }
  69. } else {
  70. if (!box.computed_values().width().is_undefined_or_auto() || !box.computed_values().min_width().is_undefined_or_auto()) {
  71. cross_constrained = true;
  72. }
  73. }
  74. if (!cross_constrained && box.children_are_inline()) {
  75. auto& block_container = verify_cast<BlockContainer>(box);
  76. BlockFormattingContext bfc(block_container, this);
  77. bfc.run(box, LayoutMode::Default);
  78. InlineFormattingContext ifc(block_container, &bfc);
  79. ifc.run(box, LayoutMode::OnlyRequiredLineBreaks);
  80. if (is_row)
  81. return box.height();
  82. return box.width();
  83. }
  84. if (is_row) {
  85. return BlockFormattingContext::compute_theoretical_height(box);
  86. } else {
  87. BlockFormattingContext context(verify_cast<BlockContainer>(box), this);
  88. context.compute_width(box);
  89. return box.width();
  90. }
  91. };
  92. Vector<FlexItem> flex_items;
  93. // 1. Generate anonymous flex items
  94. generate_anonymous_flex_items(flex_container, flex_items);
  95. // 2. Determine the available main and cross space for the flex items
  96. float main_max_size = NumericLimits<float>::max();
  97. float main_min_size = 0;
  98. float cross_max_size = NumericLimits<float>::max();
  99. float cross_min_size = 0;
  100. bool main_is_constrained = false;
  101. bool cross_is_constrained = false;
  102. bool main_size_is_infinite = false;
  103. auto available_space = determine_available_main_and_cross_space(flex_container, main_size_is_infinite, main_is_constrained, cross_is_constrained, main_min_size, main_max_size, cross_min_size, cross_max_size);
  104. auto main_available_size = available_space.main;
  105. [[maybe_unused]] auto cross_available_size = available_space.cross;
  106. // 3. Determine the flex base size and hypothetical main size of each item
  107. for (auto& flex_item : flex_items) {
  108. determine_flex_base_size_and_hypothetical_main_size(flex_container, flex_item);
  109. }
  110. // 4. Determine the main size of the flex container
  111. determine_main_size_of_flex_container(flex_container, flex_items, main_is_constrained, main_size_is_infinite, main_available_size, main_min_size, main_max_size);
  112. // 5. Collect flex items into flex lines:
  113. // After this step no additional items are to be added to flex_lines or any of its items!
  114. auto flex_lines = collect_flex_items_into_flex_lines(flex_container, flex_items, main_available_size);
  115. // 6. Resolve the flexible lengths https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths
  116. enum FlexFactor {
  117. FlexGrowFactor,
  118. FlexShrinkFactor
  119. };
  120. FlexFactor used_flex_factor;
  121. // 6.1. Determine used flex factor
  122. for (auto& flex_line : flex_lines) {
  123. size_t number_of_unfrozen_items_on_line = flex_line.items.size();
  124. float sum_of_hypothetical_main_sizes = 0;
  125. for (auto& flex_item : flex_line.items) {
  126. sum_of_hypothetical_main_sizes += flex_item->hypothetical_main_size;
  127. }
  128. if (sum_of_hypothetical_main_sizes < main_available_size)
  129. used_flex_factor = FlexFactor::FlexGrowFactor;
  130. else
  131. used_flex_factor = FlexFactor::FlexShrinkFactor;
  132. for (auto& flex_item : flex_line.items) {
  133. if (used_flex_factor == FlexFactor::FlexGrowFactor)
  134. flex_item->flex_factor = flex_item->box.computed_values().flex_grow_factor();
  135. else if (used_flex_factor == FlexFactor::FlexShrinkFactor)
  136. flex_item->flex_factor = flex_item->box.computed_values().flex_shrink_factor();
  137. }
  138. // 6.2. Size inflexible items
  139. auto freeze_item_setting_target_main_size_to_hypothetical_main_size = [&number_of_unfrozen_items_on_line](FlexItem& item) {
  140. item.target_main_size = item.hypothetical_main_size;
  141. number_of_unfrozen_items_on_line--;
  142. item.frozen = true;
  143. };
  144. for (auto& flex_item : flex_line.items) {
  145. if (flex_item->flex_factor.has_value() && flex_item->flex_factor.value() == 0) {
  146. freeze_item_setting_target_main_size_to_hypothetical_main_size(*flex_item);
  147. } else if (used_flex_factor == FlexFactor::FlexGrowFactor) {
  148. // FIXME: Spec doesn't include the == case, but we take a too basic approach to calculating the values used so this is appropriate
  149. if (flex_item->flex_base_size > flex_item->hypothetical_main_size) {
  150. freeze_item_setting_target_main_size_to_hypothetical_main_size(*flex_item);
  151. }
  152. } else if (used_flex_factor == FlexFactor::FlexShrinkFactor) {
  153. if (flex_item->flex_base_size < flex_item->hypothetical_main_size) {
  154. freeze_item_setting_target_main_size_to_hypothetical_main_size(*flex_item);
  155. }
  156. }
  157. }
  158. // 6.3. Calculate initial free space
  159. auto calculate_free_space = [&]() {
  160. float sum_of_items_on_line = 0;
  161. for (auto& flex_item : flex_line.items) {
  162. if (flex_item->frozen)
  163. sum_of_items_on_line += flex_item->target_main_size;
  164. else
  165. sum_of_items_on_line += flex_item->flex_base_size;
  166. }
  167. return main_available_size - sum_of_items_on_line;
  168. };
  169. float initial_free_space = calculate_free_space();
  170. // 6.4 Loop
  171. auto for_each_unfrozen_item = [&flex_line](auto callback) {
  172. for (auto& flex_item : flex_line.items) {
  173. if (!flex_item->frozen)
  174. callback(flex_item);
  175. }
  176. };
  177. while (number_of_unfrozen_items_on_line > 0) {
  178. // b Calculate the remaining free space
  179. auto remaining_free_space = calculate_free_space();
  180. float sum_of_unfrozen_flex_items_flex_factors = 0;
  181. for_each_unfrozen_item([&](FlexItem* item) {
  182. sum_of_unfrozen_flex_items_flex_factors += item->flex_factor.value_or(1);
  183. });
  184. if (sum_of_unfrozen_flex_items_flex_factors < 1) {
  185. auto intermediate_free_space = initial_free_space * sum_of_unfrozen_flex_items_flex_factors;
  186. if (AK::abs(intermediate_free_space) < AK::abs(remaining_free_space))
  187. remaining_free_space = intermediate_free_space;
  188. }
  189. // c Distribute free space proportional to the flex factors
  190. if (remaining_free_space != 0) {
  191. if (used_flex_factor == FlexFactor::FlexGrowFactor) {
  192. float sum_of_flex_grow_factor_of_unfrozen_items = sum_of_unfrozen_flex_items_flex_factors;
  193. for_each_unfrozen_item([&](FlexItem* flex_item) {
  194. float ratio = flex_item->flex_factor.value_or(1) / sum_of_flex_grow_factor_of_unfrozen_items;
  195. flex_item->target_main_size = flex_item->flex_base_size + (remaining_free_space * ratio);
  196. });
  197. } else if (used_flex_factor == FlexFactor::FlexShrinkFactor) {
  198. float sum_of_scaled_flex_shrink_factor_of_unfrozen_items = 0;
  199. for_each_unfrozen_item([&](FlexItem* flex_item) {
  200. flex_item->scaled_flex_shrink_factor = flex_item->flex_factor.value_or(1) * flex_item->flex_base_size;
  201. sum_of_scaled_flex_shrink_factor_of_unfrozen_items += flex_item->scaled_flex_shrink_factor;
  202. });
  203. for_each_unfrozen_item([&](FlexItem* flex_item) {
  204. float ratio = 1.0f;
  205. if (sum_of_scaled_flex_shrink_factor_of_unfrozen_items != 0.0f)
  206. ratio = flex_item->scaled_flex_shrink_factor / sum_of_scaled_flex_shrink_factor_of_unfrozen_items;
  207. flex_item->target_main_size = flex_item->flex_base_size - (AK::abs(remaining_free_space) * ratio);
  208. });
  209. }
  210. } else {
  211. // This isn't spec but makes sense.
  212. for_each_unfrozen_item([&](FlexItem* flex_item) {
  213. flex_item->target_main_size = flex_item->flex_base_size;
  214. });
  215. }
  216. // d Fix min/max violations.
  217. float adjustments = 0.0f;
  218. for_each_unfrozen_item([&](FlexItem* item) {
  219. auto min_main = has_main_min_size(item->box)
  220. ? specified_main_min_size(item->box)
  221. : 0;
  222. auto max_main = has_main_max_size(item->box)
  223. ? specified_main_max_size(item->box)
  224. : NumericLimits<float>::max();
  225. float original_target_size = item->target_main_size;
  226. if (item->target_main_size < min_main) {
  227. item->target_main_size = min_main;
  228. item->is_min_violation = true;
  229. }
  230. if (item->target_main_size > max_main) {
  231. item->target_main_size = max_main;
  232. item->is_max_violation = true;
  233. }
  234. float delta = item->target_main_size - original_target_size;
  235. adjustments += delta;
  236. });
  237. // e Freeze over-flexed items
  238. float total_violation = adjustments;
  239. if (total_violation == 0) {
  240. for_each_unfrozen_item([&](FlexItem* item) {
  241. --number_of_unfrozen_items_on_line;
  242. item->frozen = true;
  243. });
  244. } else if (total_violation > 0) {
  245. for_each_unfrozen_item([&](FlexItem* item) {
  246. if (item->is_min_violation) {
  247. --number_of_unfrozen_items_on_line;
  248. item->frozen = true;
  249. }
  250. });
  251. } else if (total_violation < 0) {
  252. for_each_unfrozen_item([&](FlexItem* item) {
  253. if (item->is_max_violation) {
  254. --number_of_unfrozen_items_on_line;
  255. item->frozen = true;
  256. }
  257. });
  258. }
  259. }
  260. // 6.5.
  261. for (auto& flex_item : flex_line.items) {
  262. flex_item->main_size = flex_item->target_main_size;
  263. };
  264. }
  265. // Cross Size Determination
  266. // 7. Determine the hypothetical cross size of each item
  267. for (auto& flex_item : flex_items) {
  268. flex_item.hypothetical_cross_size = calculate_hypothetical_cross_size(flex_item.box);
  269. }
  270. // 8. Calculate the cross size of each flex line.
  271. if (flex_lines.size() == 1 && has_definite_cross_size(flex_container)) {
  272. flex_lines[0].cross_size = specified_cross_size(flex_container);
  273. } else {
  274. for (auto& flex_line : flex_lines) {
  275. // FIXME: Implement 8.1
  276. // FIXME: This isn't spec but makes sense here
  277. if (has_definite_cross_size(flex_container) && flex_container.computed_values().align_items() == CSS::AlignItems::Stretch) {
  278. flex_line.cross_size = specified_cross_size(flex_container) / flex_lines.size();
  279. continue;
  280. }
  281. // 8.2
  282. float largest_hypothetical_cross_size = 0;
  283. for (auto& flex_item : flex_line.items) {
  284. if (largest_hypothetical_cross_size < flex_item->hypothetical_cross_size_with_margins())
  285. largest_hypothetical_cross_size = flex_item->hypothetical_cross_size_with_margins();
  286. }
  287. // 8.3
  288. flex_line.cross_size = max(0.0f, largest_hypothetical_cross_size);
  289. }
  290. if (flex_lines.size() == 1) {
  291. clamp(flex_lines[0].cross_size, cross_min_size, cross_max_size);
  292. }
  293. }
  294. // 9. Handle 'align-content: stretch'.
  295. // FIXME: This
  296. // 10. Collapse visibility:collapse items.
  297. // FIXME: This
  298. // 11. Determine the used cross size of each flex item.
  299. // FIXME: Get the alignment via "align-self" of the item (which accesses "align-items" of the parent if unset)
  300. for (auto& flex_line : flex_lines) {
  301. for (auto& flex_item : flex_line.items) {
  302. if (is_cross_auto(flex_item->box) && flex_container.computed_values().align_items() == CSS::AlignItems::Stretch) {
  303. flex_item->cross_size = flex_line.cross_size;
  304. } else {
  305. flex_item->cross_size = flex_item->hypothetical_cross_size;
  306. }
  307. }
  308. }
  309. // 12. Distribute any remaining free space.
  310. for (auto& flex_line : flex_lines) {
  311. // 12.1.
  312. float used_main_space = 0;
  313. size_t auto_margins = 0;
  314. for (auto& flex_item : flex_line.items) {
  315. used_main_space += flex_item->main_size;
  316. if (is_main_axis_margin_first_auto(flex_item->box))
  317. ++auto_margins;
  318. if (is_main_axis_margin_second_auto(flex_item->box))
  319. ++auto_margins;
  320. }
  321. float remaining_free_space = main_available_size - used_main_space;
  322. if (remaining_free_space > 0) {
  323. float size_per_auto_margin = remaining_free_space / (float)auto_margins;
  324. for (auto& flex_item : flex_line.items) {
  325. if (is_main_axis_margin_first_auto(flex_item->box))
  326. set_main_axis_first_margin(flex_item->box, size_per_auto_margin);
  327. if (is_main_axis_margin_second_auto(flex_item->box))
  328. set_main_axis_second_margin(flex_item->box, size_per_auto_margin);
  329. }
  330. } else {
  331. for (auto& flex_item : flex_line.items) {
  332. if (is_main_axis_margin_first_auto(flex_item->box))
  333. set_main_axis_first_margin(flex_item->box, 0);
  334. if (is_main_axis_margin_second_auto(flex_item->box))
  335. set_main_axis_second_margin(flex_item->box, 0);
  336. }
  337. }
  338. // 12.2.
  339. float space_between_items = 0;
  340. float space_before_first_item = 0;
  341. auto number_of_items = flex_line.items.size();
  342. switch (flex_container.computed_values().justify_content()) {
  343. case CSS::JustifyContent::FlexStart:
  344. break;
  345. case CSS::JustifyContent::FlexEnd:
  346. space_before_first_item = main_available_size - used_main_space;
  347. break;
  348. case CSS::JustifyContent::Center:
  349. space_before_first_item = (main_available_size - used_main_space) / 2.0f;
  350. break;
  351. case CSS::JustifyContent::SpaceBetween:
  352. space_between_items = remaining_free_space / (number_of_items - 1);
  353. break;
  354. case CSS::JustifyContent::SpaceAround:
  355. space_between_items = remaining_free_space / number_of_items;
  356. space_before_first_item = space_between_items / 2.0f;
  357. break;
  358. }
  359. // FIXME: Support reverse
  360. float main_offset = space_before_first_item;
  361. for (auto& flex_item : flex_line.items) {
  362. flex_item->main_offset = main_offset;
  363. main_offset += flex_item->main_size + space_between_items;
  364. }
  365. }
  366. // 13. Resolve cross-axis auto margins.
  367. // FIXME: This
  368. // 14. Align all flex items along the cross-axis
  369. // FIXME: Get the alignment via "align-self" of the item (which accesses "align-items" of the parent if unset)
  370. // FIXME: Take better care of margins
  371. float line_cross_offset = 0;
  372. for (auto& flex_line : flex_lines) {
  373. for (auto* flex_item : flex_line.items) {
  374. switch (flex_container.computed_values().align_items()) {
  375. case CSS::AlignItems::Baseline:
  376. // FIXME: Implement this
  377. // Fallthrough
  378. case CSS::AlignItems::FlexStart:
  379. case CSS::AlignItems::Stretch:
  380. flex_item->cross_offset = line_cross_offset + flex_item->margins.cross_before;
  381. break;
  382. case CSS::AlignItems::FlexEnd:
  383. flex_item->cross_offset = line_cross_offset + flex_line.cross_size - flex_item->cross_size;
  384. break;
  385. case CSS::AlignItems::Center:
  386. flex_item->cross_offset = line_cross_offset + (flex_line.cross_size / 2.0f) - (flex_item->cross_size / 2.0f);
  387. break;
  388. default:
  389. break;
  390. }
  391. }
  392. line_cross_offset += flex_line.cross_size;
  393. }
  394. // 15. Determine the flex container’s used cross size:
  395. if (has_definite_cross_size(flex_container)) {
  396. float clamped_cross_size = clamp(specified_cross_size(flex_container), cross_min_size, cross_max_size);
  397. set_cross_size(flex_container, clamped_cross_size);
  398. } else {
  399. float sum_of_flex_lines_cross_sizes = 0;
  400. for (auto& flex_line : flex_lines) {
  401. sum_of_flex_lines_cross_sizes += flex_line.cross_size;
  402. }
  403. float clamped_cross_size = clamp(sum_of_flex_lines_cross_sizes, cross_min_size, cross_max_size);
  404. set_cross_size(flex_container, clamped_cross_size);
  405. }
  406. // 16. Align all flex lines
  407. // FIXME: Support align-content
  408. // FIXME: Support reverse
  409. for (auto& flex_line : flex_lines) {
  410. for (auto* flex_item : flex_line.items) {
  411. set_main_size(flex_item->box, flex_item->main_size);
  412. set_cross_size(flex_item->box, flex_item->cross_size);
  413. set_offset(flex_item->box, flex_item->main_offset, flex_item->cross_offset);
  414. }
  415. }
  416. }
  417. static void populate_specified_margins(FlexItem& item, CSS::FlexDirection flex_direction)
  418. {
  419. auto width_of_containing_block = item.box.width_of_logical_containing_block();
  420. // FIXME: This should also take reverse-ness into account
  421. if (flex_direction == CSS::FlexDirection::Row || flex_direction == CSS::FlexDirection::RowReverse) {
  422. item.margins.main_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  423. item.margins.main_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  424. item.margins.cross_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  425. item.margins.cross_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  426. } else {
  427. item.margins.main_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  428. item.margins.main_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  429. item.margins.cross_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  430. item.margins.cross_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
  431. }
  432. };
  433. // https://www.w3.org/TR/css-flexbox-1/#flex-items
  434. void FlexFormattingContext::generate_anonymous_flex_items(Box& flex_container, Vector<FlexItem>& flex_items)
  435. {
  436. // More like, sift through the already generated items.
  437. // After this step no items are to be added or removed from flex_items!
  438. // It holds every item we need to consider and there should be nothing in the following
  439. // calculations that could change that.
  440. // This is particularly important since we take references to the items stored in flex_items
  441. // later, whose addresses won't be stable if we added or removed any items.
  442. if (!flex_container.has_definite_width()) {
  443. flex_container.set_width(flex_container.containing_block()->width());
  444. } else {
  445. flex_container.set_width(flex_container.computed_values().width().resolved_or_zero(flex_container, flex_container.containing_block()->width()).to_px(flex_container));
  446. }
  447. if (!flex_container.has_definite_height()) {
  448. flex_container.set_height(flex_container.containing_block()->height());
  449. } else {
  450. flex_container.set_height(flex_container.computed_values().height().resolved_or_zero(flex_container, flex_container.containing_block()->height()).to_px(flex_container));
  451. }
  452. flex_container.for_each_child_of_type<Box>([&](Box& child_box) {
  453. layout_inside(child_box, LayoutMode::Default);
  454. // Skip anonymous text runs that are only whitespace.
  455. if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {
  456. bool contains_only_white_space = true;
  457. child_box.for_each_in_inclusive_subtree_of_type<TextNode>([&contains_only_white_space](auto& text_node) {
  458. if (!text_node.text_for_rendering().is_whitespace()) {
  459. contains_only_white_space = false;
  460. return IterationDecision::Break;
  461. }
  462. return IterationDecision::Continue;
  463. });
  464. if (contains_only_white_space)
  465. return IterationDecision::Continue;
  466. }
  467. // Skip any "out-of-flow" children
  468. if (child_box.is_out_of_flow(*this))
  469. return IterationDecision::Continue;
  470. child_box.set_flex_item(true);
  471. FlexItem flex_item = { child_box };
  472. populate_specified_margins(flex_item, m_flex_direction);
  473. flex_items.append(move(flex_item));
  474. return IterationDecision::Continue;
  475. });
  476. }
  477. bool FlexFormattingContext::has_definite_main_size(Box const& box) const
  478. {
  479. return is_row_layout() ? box.has_definite_width() : box.has_definite_height();
  480. }
  481. float FlexFormattingContext::specified_main_size(Box const& box) const
  482. {
  483. return is_row_layout() ? box.width() : box.height();
  484. }
  485. float FlexFormattingContext::specified_cross_size(Box const& box) const
  486. {
  487. return is_row_layout() ? box.height() : box.width();
  488. }
  489. bool FlexFormattingContext::has_main_min_size(Box const& box) const
  490. {
  491. auto value = is_row_layout() ? box.computed_values().min_width() : box.computed_values().min_height();
  492. return !value.is_undefined_or_auto();
  493. }
  494. bool FlexFormattingContext::has_cross_min_size(Box const& box) const
  495. {
  496. auto value = is_row_layout() ? box.computed_values().min_height() : box.computed_values().min_width();
  497. return !value.is_undefined_or_auto();
  498. }
  499. bool FlexFormattingContext::has_definite_cross_size(Box const& box) const
  500. {
  501. return (is_row_layout() ? box.has_definite_height() : box.has_definite_width()) && cross_size_is_absolute_or_resolved_nicely(box);
  502. }
  503. bool FlexFormattingContext::cross_size_is_absolute_or_resolved_nicely(NodeWithStyle const& box) const
  504. {
  505. auto length = is_row_layout() ? box.computed_values().height() : box.computed_values().width();
  506. if (length.is_absolute() || length.is_relative())
  507. return true;
  508. if (length.is_undefined_or_auto())
  509. return false;
  510. if (!box.parent())
  511. return false;
  512. if (length.is_percentage() && cross_size_is_absolute_or_resolved_nicely(*box.parent()))
  513. return true;
  514. return false;
  515. }
  516. float FlexFormattingContext::specified_main_size_of_child_box(Box const& flex_container, Box const& child_box) const
  517. {
  518. auto main_size_of_parent = specified_main_size(flex_container);
  519. auto value = is_row_layout() ? child_box.computed_values().width() : child_box.computed_values().height();
  520. return value.resolved_or_zero(child_box, main_size_of_parent).to_px(child_box);
  521. }
  522. float FlexFormattingContext::specified_main_min_size(Box const& box) const
  523. {
  524. return is_row_layout()
  525. ? get_pixel_size(box, box.computed_values().min_width())
  526. : get_pixel_size(box, box.computed_values().min_height());
  527. }
  528. float FlexFormattingContext::specified_cross_min_size(Box const& box) const
  529. {
  530. return is_row_layout()
  531. ? get_pixel_size(box, box.computed_values().min_height())
  532. : get_pixel_size(box, box.computed_values().min_width());
  533. }
  534. bool FlexFormattingContext::has_main_max_size(Box const& box) const
  535. {
  536. return is_row_layout()
  537. ? !box.computed_values().max_width().is_undefined_or_auto()
  538. : !box.computed_values().max_height().is_undefined_or_auto();
  539. }
  540. bool FlexFormattingContext::has_cross_max_size(Box const& box) const
  541. {
  542. return is_row_layout()
  543. ? !box.computed_values().max_height().is_undefined_or_auto()
  544. : !box.computed_values().max_width().is_undefined_or_auto();
  545. }
  546. float FlexFormattingContext::specified_main_max_size(Box const& box) const
  547. {
  548. return is_row_layout()
  549. ? get_pixel_size(box, box.computed_values().max_width())
  550. : get_pixel_size(box, box.computed_values().max_height());
  551. }
  552. float FlexFormattingContext::specified_cross_max_size(Box const& box) const
  553. {
  554. return is_row_layout()
  555. ? get_pixel_size(box, box.computed_values().max_height())
  556. : get_pixel_size(box, box.computed_values().max_width());
  557. }
  558. float FlexFormattingContext::calculated_main_size(Box const& box) const
  559. {
  560. return is_row_layout() ? box.width() : box.height();
  561. }
  562. bool FlexFormattingContext::is_cross_auto(Box const& box) const
  563. {
  564. return is_row_layout() ? box.computed_values().height().is_auto() : box.computed_values().width().is_auto();
  565. }
  566. bool FlexFormattingContext::is_main_axis_margin_first_auto(Box const& box) const
  567. {
  568. return is_row_layout() ? box.computed_values().margin().left.is_auto() : box.computed_values().margin().top.is_auto();
  569. }
  570. bool FlexFormattingContext::is_main_axis_margin_second_auto(Box const& box) const
  571. {
  572. return is_row_layout() ? box.computed_values().margin().right.is_auto() : box.computed_values().margin().bottom.is_auto();
  573. }
  574. void FlexFormattingContext::set_main_size(Box& box, float size)
  575. {
  576. if (is_row_layout())
  577. box.set_width(size);
  578. else
  579. box.set_height(size);
  580. }
  581. void FlexFormattingContext::set_cross_size(Box& box, float size)
  582. {
  583. if (is_row_layout())
  584. box.set_height(size);
  585. else
  586. box.set_width(size);
  587. }
  588. void FlexFormattingContext::set_offset(Box& box, float main_offset, float cross_offset)
  589. {
  590. if (is_row_layout())
  591. box.set_offset(main_offset, cross_offset);
  592. else
  593. box.set_offset(cross_offset, main_offset);
  594. }
  595. void FlexFormattingContext::set_main_axis_first_margin(Box& box, float margin)
  596. {
  597. if (is_row_layout())
  598. box.box_model().margin.left = margin;
  599. else
  600. box.box_model().margin.top = margin;
  601. }
  602. void FlexFormattingContext::set_main_axis_second_margin(Box& box, float margin)
  603. {
  604. if (is_row_layout())
  605. box.box_model().margin.right = margin;
  606. else
  607. box.box_model().margin.bottom = margin;
  608. }
  609. float FlexFormattingContext::sum_of_margin_padding_border_in_main_axis(Box const& box) const
  610. {
  611. auto& margin = box.box_model().margin;
  612. auto& padding = box.box_model().padding;
  613. auto& border = box.box_model().border;
  614. if (is_row_layout()) {
  615. return margin.left + margin.right
  616. + padding.left + padding.right
  617. + border.left + border.right;
  618. } else {
  619. return margin.top + margin.bottom
  620. + padding.top + padding.bottom
  621. + border.top + border.bottom;
  622. }
  623. }
  624. // https://www.w3.org/TR/css-flexbox-1/#algo-available
  625. FlexFormattingContext::AvailableSpace FlexFormattingContext::determine_available_main_and_cross_space(const Box& flex_container, bool& main_size_is_infinite, bool& main_is_constrained, bool& cross_is_constrained, float& main_min_size, float& main_max_size, float& cross_min_size, float& cross_max_size) const
  626. {
  627. auto containing_block_effective_main_size = [&](Box const& box) {
  628. if (is_row_layout()) {
  629. if (box.containing_block()->has_definite_width())
  630. return box.containing_block()->width();
  631. main_size_is_infinite = true;
  632. return NumericLimits<float>::max();
  633. } else {
  634. if (box.containing_block()->has_definite_height())
  635. return box.containing_block()->height();
  636. main_size_is_infinite = true;
  637. return NumericLimits<float>::max();
  638. }
  639. };
  640. float main_available_space = 0;
  641. float cross_available_space = 0;
  642. if (has_definite_main_size(flex_container)) {
  643. main_is_constrained = true;
  644. main_available_space = specified_main_size(flex_container);
  645. } else {
  646. if (has_main_max_size(flex_container)) {
  647. main_max_size = specified_main_max_size(flex_container);
  648. main_available_space = main_max_size;
  649. main_is_constrained = true;
  650. }
  651. if (has_main_min_size(flex_container)) {
  652. main_min_size = specified_main_min_size(flex_container);
  653. main_is_constrained = true;
  654. }
  655. if (!main_is_constrained) {
  656. auto available_main_size = containing_block_effective_main_size(flex_container);
  657. main_available_space = available_main_size - sum_of_margin_padding_border_in_main_axis(flex_container);
  658. if (flex_container.computed_values().flex_wrap() == CSS::FlexWrap::Wrap || flex_container.computed_values().flex_wrap() == CSS::FlexWrap::WrapReverse) {
  659. main_available_space = specified_main_size(*flex_container.containing_block());
  660. main_is_constrained = true;
  661. }
  662. }
  663. }
  664. if (has_definite_cross_size(flex_container)) {
  665. cross_available_space = specified_cross_size(flex_container);
  666. } else {
  667. if (has_cross_max_size(flex_container)) {
  668. cross_max_size = specified_cross_max_size(flex_container);
  669. cross_is_constrained = true;
  670. }
  671. if (has_cross_min_size(flex_container)) {
  672. cross_min_size = specified_cross_min_size(flex_container);
  673. cross_is_constrained = true;
  674. }
  675. // FIXME: Is this right? Probably not.
  676. if (!cross_is_constrained)
  677. cross_available_space = cross_max_size;
  678. }
  679. return AvailableSpace { .main = main_available_space, .cross = cross_available_space };
  680. }
  681. float FlexFormattingContext::layout_for_maximum_main_size(Box& box)
  682. {
  683. bool main_constrained = false;
  684. if (is_row_layout()) {
  685. if (!box.computed_values().width().is_undefined_or_auto() || !box.computed_values().min_width().is_undefined_or_auto()) {
  686. main_constrained = true;
  687. }
  688. } else {
  689. if (!box.computed_values().height().is_undefined_or_auto() || !box.computed_values().min_height().is_undefined_or_auto()) {
  690. main_constrained = true;
  691. }
  692. }
  693. if (!main_constrained && box.children_are_inline()) {
  694. auto& block_container = verify_cast<BlockContainer>(box);
  695. BlockFormattingContext bfc(block_container, this);
  696. bfc.run(box, LayoutMode::Default);
  697. InlineFormattingContext ifc(block_container, &bfc);
  698. if (is_row_layout()) {
  699. ifc.run(box, LayoutMode::OnlyRequiredLineBreaks);
  700. return box.width();
  701. } else {
  702. ifc.run(box, LayoutMode::AllPossibleLineBreaks);
  703. return box.height();
  704. }
  705. }
  706. if (is_row_layout()) {
  707. layout_inside(box, LayoutMode::OnlyRequiredLineBreaks);
  708. return box.width();
  709. } else {
  710. return BlockFormattingContext::compute_theoretical_height(box);
  711. }
  712. }
  713. // https://www.w3.org/TR/css-flexbox-1/#algo-main-item
  714. void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(Box const& flex_container, FlexItem& flex_item)
  715. {
  716. auto& child_box = flex_item.box;
  717. auto const& flex_basis = child_box.computed_values().flex_basis();
  718. if (flex_basis.type == CSS::FlexBasis::Length) {
  719. // A
  720. auto specified_base_size = get_pixel_size(child_box, flex_basis.length);
  721. if (specified_base_size == 0)
  722. flex_item.flex_base_size = calculated_main_size(flex_item.box);
  723. else
  724. flex_item.flex_base_size = specified_base_size;
  725. } else if (flex_basis.type == CSS::FlexBasis::Content
  726. && has_definite_cross_size(child_box)
  727. // FIXME: && has intrinsic aspect ratio.
  728. && false) {
  729. // B
  730. TODO();
  731. // flex_base_size is calculated from definite cross size and intrinsic aspect ratio
  732. } else if (flex_basis.type == CSS::FlexBasis::Content
  733. // FIXME: && sized under min-content or max-content contstraints
  734. && false) {
  735. // C
  736. TODO();
  737. // Size child_box under the constraints, flex_base_size is then the resulting main_size.
  738. } else if (flex_basis.type == CSS::FlexBasis::Content
  739. // FIXME: && main_size is infinite && inline axis is parallel to the main axis
  740. && false && false) {
  741. // D
  742. TODO();
  743. // Use rules for a flex_container in orthogonal flow
  744. } else {
  745. // E
  746. // FIXME: This is probably too naive.
  747. // FIXME: Care about FlexBasis::Auto
  748. if (has_definite_main_size(child_box)) {
  749. flex_item.flex_base_size = specified_main_size_of_child_box(flex_container, child_box);
  750. } else {
  751. flex_item.flex_base_size = layout_for_maximum_main_size(child_box);
  752. }
  753. }
  754. auto clamp_min = has_main_min_size(child_box)
  755. ? specified_main_min_size(child_box)
  756. : 0;
  757. auto clamp_max = has_main_max_size(child_box)
  758. ? specified_main_max_size(child_box)
  759. : NumericLimits<float>::max();
  760. flex_item.hypothetical_main_size = clamp(flex_item.flex_base_size, clamp_min, clamp_max);
  761. }
  762. // https://www.w3.org/TR/css-flexbox-1/#algo-main-container
  763. void FlexFormattingContext::determine_main_size_of_flex_container(Box& flex_container, Vector<FlexItem>& flex_items, bool const main_is_constrained, bool const main_size_is_infinite, float& main_available_size, float const main_min_size, float const main_max_size)
  764. {
  765. if ((!main_is_constrained && main_size_is_infinite) || main_available_size == 0) {
  766. // Uses https://www.w3.org/TR/css-flexbox-1/#intrinsic-main-sizes
  767. // 9.9.1
  768. // 1.
  769. float largest_max_content_flex_fraction = 0;
  770. for (auto& flex_item : flex_items) {
  771. // FIXME: This needs some serious work.
  772. float max_content_contribution = calculated_main_size(flex_item.box);
  773. float max_content_flex_fraction = max_content_contribution - flex_item.flex_base_size;
  774. if (max_content_flex_fraction > 0) {
  775. max_content_flex_fraction /= max(flex_item.box.computed_values().flex_grow_factor().value_or(1), 1.0f);
  776. } else {
  777. max_content_flex_fraction /= max(flex_item.box.computed_values().flex_shrink_factor().value_or(1), 1.0f) * flex_item.flex_base_size;
  778. }
  779. flex_item.max_content_flex_fraction = max_content_flex_fraction;
  780. if (max_content_flex_fraction > largest_max_content_flex_fraction)
  781. largest_max_content_flex_fraction = max_content_flex_fraction;
  782. }
  783. // 2. Omitted
  784. // 3.
  785. float result = 0;
  786. for (auto& flex_item : flex_items) {
  787. auto product = 0;
  788. if (flex_item.max_content_flex_fraction > 0) {
  789. product = largest_max_content_flex_fraction * flex_item.box.computed_values().flex_grow_factor().value_or(1);
  790. } else {
  791. product = largest_max_content_flex_fraction * max(flex_item.box.computed_values().flex_shrink_factor().value_or(1), 1.0f) * flex_item.flex_base_size;
  792. }
  793. result += flex_item.flex_base_size + product;
  794. }
  795. main_available_size = clamp(result, main_min_size, main_max_size);
  796. }
  797. set_main_size(flex_container, main_available_size);
  798. }
  799. // https://www.w3.org/TR/css-flexbox-1/#algo-line-break
  800. Vector<FlexLine> FlexFormattingContext::collect_flex_items_into_flex_lines(Box const& flex_container, Vector<FlexItem>& flex_items, float main_available_size)
  801. {
  802. Vector<FlexLine> flex_lines;
  803. // FIXME: Also support wrap-reverse
  804. if (flex_container.computed_values().flex_wrap() == CSS::FlexWrap::Nowrap) {
  805. FlexLine line;
  806. for (auto& flex_item : flex_items) {
  807. line.items.append(&flex_item);
  808. }
  809. flex_lines.append(line);
  810. } else {
  811. FlexLine line;
  812. float line_main_size = 0;
  813. for (auto& flex_item : flex_items) {
  814. if ((line_main_size + flex_item.hypothetical_main_size) > main_available_size) {
  815. flex_lines.append(line);
  816. line = {};
  817. line_main_size = 0;
  818. }
  819. line.items.append(&flex_item);
  820. line_main_size += flex_item.hypothetical_main_size;
  821. }
  822. flex_lines.append(line);
  823. }
  824. return flex_lines;
  825. }
  826. }