FlexFormattingContext.cpp 37 KB

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