flexbox_layout_testcases.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /*
  3. * This Source Code is subject to the terms of the Mozilla Public License
  4. * version 2.0 (the "License"). You can obtain a copy of the License at
  5. * http://mozilla.org/MPL/2.0/.
  6. */
  7. /**
  8. * For the purposes of this test, flex items are specified as a hash with a
  9. * hash-entry for each CSS property that is to be set. In these per-property
  10. * entries, the key is the property-name, and the value can be either of the
  11. * following:
  12. * (a) the property's specified value (which indicates that we don't need to
  13. * bother checking the computed value of this particular property)
  14. * ...OR...
  15. * (b) an array with 2-3 entries...
  16. * [specifiedValue, expectedComputedValue (, epsilon) ]
  17. * ...which indicates that the property's computed value should be
  18. * checked. The array's first entry (for the specified value) may be
  19. * null; this means that no value should be explicitly specified for this
  20. * property. The second entry is the property's expected computed
  21. * value. The third (optional) entry is an epsilon value, which allows for
  22. * fuzzy equality when testing the computed value.
  23. *
  24. * To allow these testcases to be re-used in both horizontal and vertical
  25. * flex containers, we specify "width"/"min-width"/etc. using the aliases
  26. * "_main-size", "_min-main-size", etc. The test code can map these
  27. * placeholder names to their corresponding property-names using the maps
  28. * defined below -- gRowPropertyMapping, gColumnPropertyMapping, etc.
  29. *
  30. * If the testcase needs to customize its flex container at all (e.g. by
  31. * specifying a custom container-size), it can do so by including a hash
  32. * called "container_properties", with propertyName:propertyValue mappings.
  33. * (This hash can use aliased property-names like "_main-size" as well.)
  34. */
  35. // The standard main-size we'll use for our flex container when setting up
  36. // the testcases defined below:
  37. var gDefaultFlexContainerSize = "200px";
  38. // Left-to-right versions of placeholder property-names used in
  39. // testcases below:
  40. var gRowPropertyMapping =
  41. {
  42. "_main-size": "width",
  43. "_min-main-size": "min-width",
  44. "_max-main-size": "max-width",
  45. "_border-main-start-width": "border-left-width",
  46. "_border-main-end-width": "border-right-width",
  47. "_padding-main-start": "padding-left",
  48. "_padding-main-end": "padding-right",
  49. "_margin-main-start": "margin-left",
  50. "_margin-main-end": "margin-right"
  51. };
  52. // Right-to-left versions of placeholder property-names used in
  53. // testcases below:
  54. var gRowReversePropertyMapping =
  55. {
  56. "_main-size": "width",
  57. "_min-main-size": "min-width",
  58. "_max-main-size": "max-width",
  59. "_border-main-start-width": "border-right-width",
  60. "_border-main-end-width": "border-left-width",
  61. "_padding-main-start": "padding-right",
  62. "_padding-main-end": "padding-left",
  63. "_margin-main-start": "margin-right",
  64. "_margin-main-end": "margin-left"
  65. };
  66. // Top-to-bottom versions of placeholder property-names used in
  67. // testcases below:
  68. var gColumnPropertyMapping =
  69. {
  70. "_main-size": "height",
  71. "_min-main-size": "min-height",
  72. "_max-main-size": "max-height",
  73. "_border-main-start-width": "border-top-width",
  74. "_border-main-end-width": "border-bottom-width",
  75. "_padding-main-start": "padding-top",
  76. "_padding-main-end": "padding-bottom",
  77. "_margin-main-start": "margin-top",
  78. "_margin-main-end": "margin-bottom"
  79. };
  80. // Bottom-to-top versions of placeholder property-names used in
  81. // testcases below:
  82. var gColumnReversePropertyMapping =
  83. {
  84. "_main-size": "height",
  85. "_min-main-size": "min-height",
  86. "_max-main-size": "max-height",
  87. "_border-main-start-width": "border-bottom-width",
  88. "_border-main-end-width": "border-top-width",
  89. "_padding-main-start": "padding-bottom",
  90. "_padding-main-end": "padding-top",
  91. "_margin-main-start": "margin-bottom",
  92. "_margin-main-end": "margin-top"
  93. };
  94. // The list of actual testcase definitions:
  95. var gFlexboxTestcases =
  96. [
  97. // No flex properties specified --> should just use 'width' for sizing
  98. {
  99. items:
  100. [
  101. { "_main-size": [ "40px", "40px" ] },
  102. { "_main-size": [ "65px", "65px" ] },
  103. ]
  104. },
  105. // flex-basis is specified:
  106. {
  107. items:
  108. [
  109. { "flex-basis": "50px",
  110. "_main-size": [ null, "50px" ]
  111. },
  112. {
  113. "flex-basis": "20px",
  114. "_main-size": [ null, "20px" ]
  115. },
  116. ]
  117. },
  118. // flex-basis is *large* -- sum of flex-basis values is > flex container size:
  119. // (w/ 0 flex-shrink so we don't shrink):
  120. {
  121. items:
  122. [
  123. {
  124. "flex": "0 0 150px",
  125. "_main-size": [ null, "150px" ]
  126. },
  127. {
  128. "flex": "0 0 90px",
  129. "_main-size": [ null, "90px" ]
  130. },
  131. ]
  132. },
  133. // flex-basis is *large* -- each flex-basis value is > flex container size:
  134. // (w/ 0 flex-shrink so we don't shrink):
  135. {
  136. items:
  137. [
  138. {
  139. "flex": "0 0 250px",
  140. "_main-size": [ null, "250px" ]
  141. },
  142. {
  143. "flex": "0 0 400px",
  144. "_main-size": [ null, "400px" ]
  145. },
  146. ]
  147. },
  148. // flex-basis has percentage value:
  149. {
  150. items:
  151. [
  152. {
  153. "flex-basis": "30%",
  154. "_main-size": [ null, "60px" ]
  155. },
  156. {
  157. "flex-basis": "45%",
  158. "_main-size": [ null, "90px" ]
  159. },
  160. ]
  161. },
  162. // flex-basis has calc(percentage) value:
  163. {
  164. items:
  165. [
  166. {
  167. "flex-basis": "calc(20%)",
  168. "_main-size": [ null, "40px" ]
  169. },
  170. {
  171. "flex-basis": "calc(80%)",
  172. "_main-size": [ null, "160px" ]
  173. },
  174. ]
  175. },
  176. // flex-basis has calc(percentage +/- length) value:
  177. {
  178. items:
  179. [
  180. {
  181. "flex-basis": "calc(10px + 20%)",
  182. "_main-size": [ null, "50px" ]
  183. },
  184. {
  185. "flex-basis": "calc(60% - 1px)",
  186. "_main-size": [ null, "119px" ]
  187. },
  188. ]
  189. },
  190. // flex-grow is specified:
  191. {
  192. items:
  193. [
  194. {
  195. "flex": "1",
  196. "_main-size": [ null, "60px" ]
  197. },
  198. {
  199. "flex": "2",
  200. "_main-size": [ null, "120px" ]
  201. },
  202. {
  203. "flex": "0 20px",
  204. "_main-size": [ null, "20px" ]
  205. }
  206. ]
  207. },
  208. // Same ratio as prev. testcase; making sure we handle float inaccuracy
  209. {
  210. items:
  211. [
  212. {
  213. "flex": "100000",
  214. "_main-size": [ null, "60px" ]
  215. },
  216. {
  217. "flex": "200000",
  218. "_main-size": [ null, "120px" ]
  219. },
  220. {
  221. "flex": "0.000001 20px",
  222. "_main-size": [ null, "20px" ]
  223. }
  224. ]
  225. },
  226. // Same ratio as prev. testcase, but with items cycled and w/
  227. // "flex: none" & explicit size instead of "flex: 0 20px"
  228. {
  229. items:
  230. [
  231. {
  232. "flex": "none",
  233. "_main-size": [ "20px", "20px" ]
  234. },
  235. {
  236. "flex": "1",
  237. "_main-size": [ null, "60px" ]
  238. },
  239. {
  240. "flex": "2",
  241. "_main-size": [ null, "120px" ]
  242. }
  243. ]
  244. },
  245. // ...and now with flex-grow:[huge] to be sure we handle infinite float values
  246. // gracefully.
  247. {
  248. items:
  249. [
  250. {
  251. "flex": "9999999999999999999999999999999999999999999999999999999",
  252. "_main-size": [ null, "200px" ]
  253. },
  254. ]
  255. },
  256. {
  257. items:
  258. [
  259. {
  260. "flex": "9999999999999999999999999999999999999999999999999999999",
  261. "_main-size": [ null, "50px" ]
  262. },
  263. {
  264. "flex": "9999999999999999999999999999999999999999999999999999999",
  265. "_main-size": [ null, "50px" ]
  266. },
  267. {
  268. "flex": "9999999999999999999999999999999999999999999999999999999",
  269. "_main-size": [ null, "50px" ]
  270. },
  271. {
  272. "flex": "9999999999999999999999999999999999999999999999999999999",
  273. "_main-size": [ null, "50px" ]
  274. },
  275. ]
  276. },
  277. {
  278. items:
  279. [
  280. {
  281. "flex": "99999999999999999999999999999999999",
  282. "_main-size": [ null, "50px" ]
  283. },
  284. {
  285. "flex": "99999999999999999999999999999999999",
  286. "_main-size": [ null, "50px" ]
  287. },
  288. {
  289. "flex": "99999999999999999999999999999999999",
  290. "_main-size": [ null, "50px" ]
  291. },
  292. {
  293. "flex": "99999999999999999999999999999999999",
  294. "_main-size": [ null, "50px" ]
  295. },
  296. ]
  297. },
  298. // And now, some testcases to check that we handle float accumulation error
  299. // gracefully.
  300. // First, a testcase with just a custom-sized huge container, to be sure we'll
  301. // be able to handle content on that scale, in the subsequent more-complex
  302. // testcases:
  303. {
  304. container_properties:
  305. {
  306. "_main-size": "9000000px"
  307. },
  308. items:
  309. [
  310. {
  311. "flex": "1",
  312. "_main-size": [ null, "9000000px" ]
  313. },
  314. ]
  315. },
  316. // ...and now with two flex items dividing up that container's huge size:
  317. {
  318. container_properties:
  319. {
  320. "_main-size": "9000000px"
  321. },
  322. items:
  323. [
  324. {
  325. "flex": "2",
  326. "_main-size": [ null, "6000000px" ]
  327. },
  328. {
  329. "flex": "1",
  330. "_main-size": [ null, "3000000px" ]
  331. },
  332. ]
  333. },
  334. // OK, now to actually test accumulation error. Below, we have six flex items
  335. // splitting up the container's size, with huge differences between flex
  336. // weights. For simplicity, I've set up the weights so that they sum exactly
  337. // to the container's size in px. So 1 unit of flex *should* get you 1px.
  338. //
  339. // NOTE: The expected computed "_main-size" values for the flex items below
  340. // appear to add up to more than their container's size, which would suggest
  341. // that they overflow their container unnecessarily. But they don't actually
  342. // overflow -- this discrepancy is simply because Gecko's code for reporting
  343. // computed-sizes rounds to 6 significant figures (in particular, the method
  344. // (nsTSubstring_CharT::AppendFloat() does this). Internally, in app-units,
  345. // the child frames' main-sizes add up exactly to the container's main-size,
  346. // as you'd hope & expect.
  347. {
  348. container_properties:
  349. {
  350. "_main-size": "9000000px"
  351. },
  352. items:
  353. [
  354. {
  355. "flex": "3000000",
  356. "_main-size": [ null, "3000000px" ]
  357. },
  358. {
  359. "flex": "1",
  360. "_main-size": [ null, "1px" ]
  361. },
  362. {
  363. "flex": "1",
  364. "_main-size": [ null, "1px" ]
  365. },
  366. {
  367. "flex": "2999999",
  368. // NOTE: Expected value is off slightly, from float error when
  369. // resolving flexible lengths & when generating computed value string:
  370. "_main-size": [ null, "3000000px" ]
  371. },
  372. {
  373. "flex": "2999998",
  374. // NOTE: Expected value is off slightly, from float error when
  375. // resolving flexible lengths & when generating computed value string:
  376. "_main-size": [ null, "3000000px" ]
  377. },
  378. {
  379. "flex": "1",
  380. "_main-size": [ null, "1px", 0.2 ]
  381. },
  382. ]
  383. },
  384. // Same flex items as previous testcase, but now reordered such that the items
  385. // with tiny flex weights are all listed last:
  386. {
  387. container_properties:
  388. {
  389. "_main-size": "9000000px"
  390. },
  391. items:
  392. [
  393. {
  394. "flex": "3000000",
  395. "_main-size": [ null, "3000000px" ]
  396. },
  397. {
  398. "flex": "2999999",
  399. // NOTE: Expected value is off slightly, from float error when
  400. // resolving flexible lengths & when generating computed value string:
  401. "_main-size": [ null, "3000000px" ]
  402. },
  403. {
  404. "flex": "2999998",
  405. // NOTE: Expected value is off slightly, from float error when
  406. // resolving flexible lengths & when generating computed value string:
  407. "_main-size": [ null, "3000000px" ]
  408. },
  409. {
  410. "flex": "1",
  411. "_main-size": [ null, "1px", 0.2 ]
  412. },
  413. {
  414. "flex": "1",
  415. "_main-size": [ null, "1px", 0.2 ]
  416. },
  417. {
  418. "flex": "1",
  419. "_main-size": [ null, "1px", 0.2 ]
  420. },
  421. ]
  422. },
  423. // Same flex items as previous testcase, but now reordered such that the items
  424. // with tiny flex weights are all listed first:
  425. {
  426. container_properties:
  427. {
  428. "_main-size": "9000000px"
  429. },
  430. items:
  431. [
  432. {
  433. "flex": "1",
  434. // NOTE: Expected value is off slightly, from float error when
  435. // resolving flexible lengths:
  436. "_main-size": [ null, "1px", 0.2 ]
  437. },
  438. {
  439. "flex": "1",
  440. // NOTE: Expected value is off slightly, from float error when
  441. // resolving flexible lengths:
  442. "_main-size": [ null, "1px", 0.2 ]
  443. },
  444. {
  445. "flex": "1",
  446. // NOTE: Expected value is off slightly, from float error when
  447. // resolving flexible lengths:
  448. "_main-size": [ null, "1px", 0.2 ]
  449. },
  450. {
  451. "flex": "3000000",
  452. "_main-size": [ null, "3000000px" ]
  453. },
  454. {
  455. "flex": "2999999",
  456. // NOTE: Expected value is off slightly, from float error when
  457. // resolving flexible lengths & when generating computed value string:
  458. "_main-size": [ null, "3000000px" ]
  459. },
  460. {
  461. "flex": "2999998",
  462. // NOTE: Expected value is off slightly, from float error when
  463. // resolving flexible lengths & when generating computed value string:
  464. "_main-size": [ null, "3000000px" ]
  465. },
  466. ]
  467. },
  468. // Trying "flex: auto" (== "1 1 auto") w/ a mix of flex-grow/flex-basis values
  469. {
  470. items:
  471. [
  472. {
  473. "flex": "auto",
  474. "_main-size": [ null, "45px" ]
  475. },
  476. {
  477. "flex": "2",
  478. "_main-size": [ null, "90px" ]
  479. },
  480. {
  481. "flex": "20px 1 0",
  482. "_main-size": [ null, "65px" ]
  483. }
  484. ]
  485. },
  486. // Same as previous, but with items cycled & different syntax
  487. {
  488. items:
  489. [
  490. {
  491. "flex": "20px",
  492. "_main-size": [ null, "65px" ]
  493. },
  494. {
  495. "flex": "1",
  496. "_main-size": [ null, "45px" ]
  497. },
  498. {
  499. "flex": "2",
  500. "_main-size": [ null, "90px" ]
  501. }
  502. ]
  503. },
  504. {
  505. items:
  506. [
  507. {
  508. "flex": "2",
  509. "_main-size": [ null, "100px" ],
  510. "border": "0px dashed",
  511. "_border-main-start-width": [ "5px", "5px" ],
  512. "_border-main-end-width": [ "15px", "15px" ],
  513. "_margin-main-start": [ "22px", "22px" ],
  514. "_margin-main-end": [ "8px", "8px" ]
  515. },
  516. {
  517. "flex": "1",
  518. "_main-size": [ null, "50px" ],
  519. "_margin-main-start": [ "auto", "0px" ],
  520. "_padding-main-end": [ "auto", "0px" ],
  521. }
  522. ]
  523. },
  524. // Test negative flexibility:
  525. // Basic testcase: just 1 item (relying on initial "flex-shrink: 1") --
  526. // should shrink to container size.
  527. {
  528. items:
  529. [
  530. { "_main-size": [ "400px", "200px" ] },
  531. ],
  532. },
  533. // ...and now with a "flex" specification and a different flex-shrink value:
  534. {
  535. items:
  536. [
  537. {
  538. "flex": "4 2 250px",
  539. "_main-size": [ null, "200px" ]
  540. },
  541. ],
  542. },
  543. // ...and now with multiple items, which all shrink proportionally (by 50%)
  544. // to fit to the container, since they have the same (initial) flex-shrink val
  545. {
  546. items:
  547. [
  548. { "_main-size": [ "80px", "40px" ] },
  549. { "_main-size": [ "40px", "20px" ] },
  550. { "_main-size": [ "30px", "15px" ] },
  551. { "_main-size": [ "250px", "125px" ] },
  552. ]
  553. },
  554. // ...and now with positive flexibility specified. (should have no effect, so
  555. // everything still shrinks by the same proportion, since the flex-shrink
  556. // values are all the same).
  557. {
  558. items:
  559. [
  560. {
  561. "flex": "4 3 100px",
  562. "_main-size": [ null, "80px" ]
  563. },
  564. {
  565. "flex": "5 3 50px",
  566. "_main-size": [ null, "40px" ]
  567. },
  568. {
  569. "flex": "0 3 100px",
  570. "_main-size": [ null, "80px" ]
  571. }
  572. ]
  573. },
  574. // ...and now with *different* flex-shrink values:
  575. {
  576. items:
  577. [
  578. {
  579. "flex": "4 2 50px",
  580. "_main-size": [ null, "30px" ]
  581. },
  582. {
  583. "flex": "5 3 50px",
  584. "_main-size": [ null, "20px" ]
  585. },
  586. {
  587. "flex": "0 0 150px",
  588. "_main-size": [ null, "150px" ]
  589. }
  590. ]
  591. },
  592. // Same ratio as prev. testcase; making sure we handle float inaccuracy
  593. {
  594. items:
  595. [
  596. {
  597. "flex": "4 20000000 50px",
  598. "_main-size": [ null, "30px" ]
  599. },
  600. {
  601. "flex": "5 30000000 50px",
  602. "_main-size": [ null, "20px" ]
  603. },
  604. {
  605. "flex": "0 0.0000001 150px",
  606. "_main-size": [ null, "150px" ]
  607. }
  608. ]
  609. },
  610. // Another "different flex-shrink values" testcase:
  611. {
  612. items:
  613. [
  614. {
  615. "flex": "4 2 115px",
  616. "_main-size": [ null, "69px" ]
  617. },
  618. {
  619. "flex": "5 1 150px",
  620. "_main-size": [ null, "120px" ]
  621. },
  622. {
  623. "flex": "1 4 30px",
  624. "_main-size": [ null, "6px" ]
  625. },
  626. {
  627. "flex": "1 0 5px",
  628. "_main-size": [ null, "5px" ]
  629. },
  630. ]
  631. },
  632. // ...and now with min-size (clamping the effects of flex-shrink on one item):
  633. {
  634. items:
  635. [
  636. {
  637. "flex": "4 5 75px",
  638. "_min-main-size": "50px",
  639. "_main-size": [ null, "50px" ],
  640. },
  641. {
  642. "flex": "5 5 100px",
  643. "_main-size": [ null, "62.5px" ]
  644. },
  645. {
  646. "flex": "0 4 125px",
  647. "_main-size": [ null, "87.5px" ]
  648. }
  649. ]
  650. },
  651. // Test a min-size that's much larger than initial preferred size, but small
  652. // enough that our flexed size pushes us over it:
  653. {
  654. items:
  655. [
  656. {
  657. "flex": "auto",
  658. "_min-main-size": "110px",
  659. "_main-size": [ "50px", "125px" ]
  660. },
  661. {
  662. "flex": "auto",
  663. "_main-size": [ null, "75px" ]
  664. }
  665. ]
  666. },
  667. // Test a min-size that's much larger than initial preferred size, and is
  668. // even larger than our positively-flexed size, so that we have to increase it
  669. // (as a 'min violation') after we've flexed.
  670. {
  671. items:
  672. [
  673. {
  674. "flex": "auto",
  675. "_min-main-size": "150px",
  676. "_main-size": [ "50px", "150px" ]
  677. },
  678. {
  679. "flex": "auto",
  680. "_main-size": [ null, "50px" ]
  681. }
  682. ]
  683. },
  684. // Test min-size on multiple items simultaneously:
  685. {
  686. items:
  687. [
  688. {
  689. "flex": "auto",
  690. "_min-main-size": "20px",
  691. "_main-size": [ null, "20px" ]
  692. },
  693. {
  694. "flex": "9 auto",
  695. "_min-main-size": "150px",
  696. "_main-size": [ "50px", "180px" ]
  697. },
  698. ]
  699. },
  700. {
  701. items:
  702. [
  703. {
  704. "flex": "1 1 0px",
  705. "_min-main-size": "90px",
  706. "_main-size": [ null, "90px" ]
  707. },
  708. {
  709. "flex": "1 1 0px",
  710. "_min-main-size": "80px",
  711. "_main-size": [ null, "80px" ]
  712. },
  713. {
  714. "flex": "1 1 40px",
  715. "_main-size": [ null, "30px" ]
  716. }
  717. ]
  718. },
  719. // Test a case where _min-main-size will be violated on different items in
  720. // successive iterations of the "resolve the flexible lengths" loop
  721. {
  722. items:
  723. [
  724. {
  725. "flex": "1 2 100px",
  726. "_min-main-size": "90px",
  727. "_main-size": [ null, "90px" ]
  728. },
  729. {
  730. "flex": "1 1 100px",
  731. "_min-main-size": "70px",
  732. "_main-size": [ null, "70px" ]
  733. },
  734. {
  735. "flex": "1 1 100px",
  736. "_main-size": [ null, "40px" ]
  737. }
  738. ]
  739. },
  740. // Test some cases that have a min-size violation on one item and a
  741. // max-size violation on another:
  742. // Here, both items initially grow to 100px. That violates both
  743. // items' sizing constraints (it's smaller than the min-size and larger than
  744. // the max-size), so we clamp both of them and sum the clamping-differences:
  745. //
  746. // (130px - 100px) + (50px - 100px) = (30px) + (-50px) = -20px
  747. //
  748. // This sum is negative, so (per spec) we freeze the item that had its
  749. // max-size violated (the second one) and restart the algorithm. This time,
  750. // all the available space (200px - 50px = 150px) goes to the not-yet-frozen
  751. // first item, and that puts it above its min-size, so all is well.
  752. {
  753. items:
  754. [
  755. {
  756. "flex": "auto",
  757. "_min-main-size": "130px",
  758. "_main-size": [ null, "150px" ]
  759. },
  760. {
  761. "flex": "auto",
  762. "_max-main-size": "50px",
  763. "_main-size": [ null, "50px" ]
  764. },
  765. ]
  766. },
  767. // As above, both items initially grow to 100px, and that violates both items'
  768. // constraints. However, now the sum of the clamping differences is:
  769. //
  770. // (130px - 100px) + (80px - 100px) = (30px) + (-20px) = 10px
  771. //
  772. // This sum is positive, so (per spec) we freeze the item that had its
  773. // min-size violated (the first one) and restart the algorithm. This time,
  774. // all the available space (200px - 130px = 70px) goes to the not-yet-frozen
  775. // second item, and that puts it below its max-size, so all is well.
  776. {
  777. items:
  778. [
  779. {
  780. "flex": "auto",
  781. "_min-main-size": "130px",
  782. "_main-size": [ null, "130px" ]
  783. },
  784. {
  785. "flex": "auto",
  786. "_max-main-size": "80px",
  787. "_main-size": [ null, "70px" ]
  788. },
  789. ]
  790. },
  791. // As above, both items initially grow to 100px, and that violates both items'
  792. // constraints. So we clamp both items and sum the clamping differences to
  793. // see what to do next. The sum is:
  794. //
  795. // (80px - 100px) + (120px - 100px) = (-20px) + (20px) = 0px
  796. //
  797. // Per spec, if the sum is 0, we're done -- we leave both items at their
  798. // clamped sizes.
  799. {
  800. items:
  801. [
  802. {
  803. "flex": "auto",
  804. "_max-main-size": "80px",
  805. "_main-size": [ null, "80px" ]
  806. },
  807. {
  808. "flex": "auto",
  809. "_min-main-size": "120px",
  810. "_main-size": [ null, "120px" ]
  811. },
  812. ]
  813. },
  814. // Test cases where flex-grow sums to less than 1:
  815. // ===============================================
  816. // This makes us treat the flexibilities like "fraction of free space"
  817. // instead of weights, so that e.g. a single item with "flex-grow: 0.1"
  818. // will only get 10% of the free space instead of all of the free space.
  819. // Basic cases where flex-grow sum is less than 1:
  820. {
  821. items:
  822. [
  823. {
  824. "flex": "0.1 100px",
  825. "_main-size": [ null, "110px" ] // +10% of free space
  826. },
  827. ]
  828. },
  829. {
  830. items:
  831. [
  832. {
  833. "flex": "0.8 0px",
  834. "_main-size": [ null, "160px" ] // +80% of free space
  835. },
  836. ]
  837. },
  838. // ... and now with two flex items:
  839. {
  840. items:
  841. [
  842. {
  843. "flex": "0.4 70px",
  844. "_main-size": [ null, "110px" ] // +40% of free space
  845. },
  846. {
  847. "flex": "0.2 30px",
  848. "_main-size": [ null, "50px" ] // +20% of free space
  849. },
  850. ]
  851. },
  852. // ...and now with max-size modifying how much free space one item can take:
  853. {
  854. items:
  855. [
  856. {
  857. "flex": "0.4 70px",
  858. "_main-size": [ null, "110px" ] // +40% of free space
  859. },
  860. {
  861. "flex": "0.2 30px",
  862. "_max-main-size": "35px",
  863. "_main-size": [ null, "35px" ] // +20% free space, then clamped
  864. },
  865. ]
  866. },
  867. // ...and now with a max-size smaller than our flex-basis:
  868. // (This makes us freeze the second item right away, before we compute
  869. // the initial free space.)
  870. {
  871. items:
  872. [
  873. {
  874. "flex": "0.4 70px",
  875. "_main-size": [ null, "118px" ] // +40% of 200px-70px-10px
  876. },
  877. {
  878. "flex": "0.2 30px",
  879. "_max-main-size": "10px",
  880. "_main-size": [ null, "10px" ] // immediately frozen
  881. },
  882. ]
  883. },
  884. // ...and now with a max-size and a huge flex-basis, such that we initially
  885. // have negative free space, which makes the "% of [original] free space"
  886. // calculations a bit more subtle. We set the "original free space" after
  887. // we've clamped the second item (the first time the free space is positive).
  888. {
  889. items:
  890. [
  891. {
  892. "flex": "0.4 70px",
  893. "_main-size": [ null, "118px" ] // +40% of free space _after freezing
  894. // the other item_
  895. },
  896. {
  897. "flex": "0.2 150px",
  898. "_max-main-size": "10px",
  899. "_main-size": [ null, "10px" ] // clamped immediately
  900. },
  901. ]
  902. },
  903. // Now with min-size modifying how much free space our items take:
  904. {
  905. items:
  906. [
  907. {
  908. "flex": "0.4 70px",
  909. "_main-size": [ null, "110px" ] // +40% of free space
  910. },
  911. {
  912. "flex": "0.2 30px",
  913. "_min-main-size": "70px",
  914. "_main-size": [ null, "70px" ] // +20% free space, then clamped
  915. },
  916. ]
  917. },
  918. // ...and now with a large enough min-size that it prevents the other flex
  919. // item from taking its full desired portion of the original free space:
  920. {
  921. items:
  922. [
  923. {
  924. "flex": "0.4 70px",
  925. "_main-size": [ null, "80px" ] // (Can't take my full +40% of
  926. // free space due to other item's
  927. // large min-size.)
  928. },
  929. {
  930. "flex": "0.2 30px",
  931. "_min-main-size": "120px",
  932. "_main-size": [ null, "120px" ] // +20% free space, then clamped
  933. },
  934. ]
  935. },
  936. // ...and now with a large-enough min-size that it pushes the other flex item
  937. // to actually shrink a bit (with default "flex-shrink:1"):
  938. {
  939. items:
  940. [
  941. {
  942. "flex": "0.3 30px",
  943. "_main-size": [ null, "20px" ] // -10px, instead of desired +45px
  944. },
  945. {
  946. "flex": "0.2 20px",
  947. "_min-main-size": "180px",
  948. "_main-size": [ null, "180px" ] // +160px, instead of desired +30px
  949. },
  950. ]
  951. },
  952. // In this case, the items' flexibilities don't initially sum to < 1, but they
  953. // do after we freeze the third item for violating its max-size.
  954. {
  955. items:
  956. [
  957. {
  958. "flex": "0.3 30px",
  959. "_main-size": [ null, "75px" ]
  960. // 1st loop: desires (0.3 / 5) * 150px = 9px. Tentatively granted.
  961. // 2nd loop: desires 0.3 * 150px = 45px. Tentatively granted.
  962. // 3rd loop: desires 0.3 * 150px = 45px. Granted +45px.
  963. },
  964. {
  965. "flex": "0.2 20px",
  966. "_max-main-size": "30px",
  967. "_main-size": [ null, "30px" ]
  968. // First loop: desires (0.2 / 5) * 150px = 6px. Tentatively granted.
  969. // Second loop: desires 0.2 * 150px = 30px. Frozen at +10px.
  970. },
  971. {
  972. "flex": "4.5 0px",
  973. "_max-main-size": "20px",
  974. "_main-size": [ null, "20px" ]
  975. // First loop: desires (4.5 / 5) * 150px = 135px. Frozen at +20px.
  976. },
  977. ]
  978. },
  979. // Make sure we calculate "original free space" correctly when one of our
  980. // flex items will be clamped right away, due to max-size preventing it from
  981. // growing at all:
  982. {
  983. // Here, the second flex item is effectively inflexible; it's
  984. // immediately frozen at 40px since we're growing & this item's max size
  985. // trivially prevents it from growing. This leaves us with an "original
  986. // free space" of 60px. The first flex item takes half of that, due to
  987. // its flex-grow value of 0.5.
  988. items:
  989. [
  990. {
  991. "flex": "0.5 100px",
  992. "_main-size": [ null, "130px" ]
  993. },
  994. {
  995. "flex": "1 98px",
  996. "_max-main-size": "40px",
  997. "_main-size": [ null, "40px" ]
  998. },
  999. ]
  1000. },
  1001. {
  1002. // Same as previous example, but with a larger flex-basis on the second
  1003. // element (which shouldn't ultimately matter, because its max size clamps
  1004. // its size immediately anyway).
  1005. items:
  1006. [
  1007. {
  1008. "flex": "0.5 100px",
  1009. "_main-size": [ null, "130px" ]
  1010. },
  1011. {
  1012. "flex": "1 101px",
  1013. "_max-main-size": "40px",
  1014. "_main-size": [ null, "40px" ]
  1015. },
  1016. ]
  1017. },
  1018. {
  1019. // Here, the third flex item is effectively inflexible; it's immediately
  1020. // frozen at 0px since we're growing & this item's max size trivially
  1021. // prevents it from growing. This leaves us with an "original free space" of
  1022. // 100px. The first flex item takes 40px, and the third takes 50px, due to
  1023. // their flex values of 0.4 and 0.5.
  1024. items:
  1025. [
  1026. {
  1027. "flex": "0.4 50px",
  1028. "_main-size": [ null, "90px" ]
  1029. },
  1030. {
  1031. "flex": "0.5 50px",
  1032. "_main-size": [ null, "100px" ]
  1033. },
  1034. {
  1035. "flex": "0 90px",
  1036. "_max-main-size": "0px",
  1037. "_main-size": [ null, "0px" ]
  1038. },
  1039. ]
  1040. },
  1041. {
  1042. // Same as previous example, but with slightly larger flex-grow values on
  1043. // the first and second items, which sum to 1.0 and produce slightly larger
  1044. // main sizes. This demonstrates that there's no discontinuity between the
  1045. // "< 1.0 sum" to ">= 1.0 sum" behavior, in this situation at least.
  1046. items:
  1047. [
  1048. {
  1049. "flex": "0.45 50px",
  1050. "_main-size": [ null, "95px" ]
  1051. },
  1052. {
  1053. "flex": "0.55 50px",
  1054. "_main-size": [ null, "105px" ]
  1055. },
  1056. {
  1057. "flex": "0 90px",
  1058. "_max-main-size": "0px",
  1059. "_main-size": [ null, "0px" ]
  1060. },
  1061. ]
  1062. },
  1063. // Test cases where flex-shrink sums to less than 1:
  1064. // =================================================
  1065. // This makes us treat the flexibilities more like "fraction of (negative)
  1066. // free space" instead of weights, so that e.g. a single item with
  1067. // "flex-shrink: 0.1" will only shrink by 10% of amount that it overflows
  1068. // its container by.
  1069. //
  1070. // It gets a bit more complex when there are multiple flex items, because
  1071. // flex-shrink is scaled by the flex-basis before it's used as a weight. But
  1072. // even with that scaling, the general principal is that e.g. if the
  1073. // flex-shrink values *sum* to 0.6, then the items will collectively only
  1074. // shrink by 60% (and hence will still overflow).
  1075. // Basic cases where flex-grow sum is less than 1:
  1076. {
  1077. items:
  1078. [
  1079. {
  1080. "flex": "0 0.1 300px",
  1081. "_main-size": [ null, "290px" ] // +10% of (negative) free space
  1082. },
  1083. ]
  1084. },
  1085. {
  1086. items:
  1087. [
  1088. {
  1089. "flex": "0 0.8 400px",
  1090. "_main-size": [ null, "240px" ] // +80% of (negative) free space
  1091. },
  1092. ]
  1093. },
  1094. // ...now with two flex items, with the same flex-basis value:
  1095. {
  1096. items:
  1097. [
  1098. {
  1099. "flex": "0 0.4 150px",
  1100. "_main-size": [ null, "110px" ] // +40% of (negative) free space
  1101. },
  1102. {
  1103. "flex": "0 0.2 150px",
  1104. "_main-size": [ null, "130px" ] // +20% of (negative) free space
  1105. },
  1106. ]
  1107. },
  1108. // ...now with two flex items, with different flex-basis values (and hence
  1109. // differently-scaled flex factors):
  1110. {
  1111. items:
  1112. [
  1113. {
  1114. "flex": "0 0.3 100px",
  1115. "_main-size": [ null, "76px" ]
  1116. },
  1117. {
  1118. "flex": "0 0.1 200px",
  1119. "_main-size": [ null, "184px" ]
  1120. }
  1121. ]
  1122. // Notes:
  1123. // - Free space: -100px
  1124. // - Sum of flex-shrink factors: 0.3 + 0.1 = 0.4
  1125. // - Since that sum ^ is < 1, we'll only distribute that fraction of
  1126. // the free space. We'll distribute: -100px * 0.4 = -40px
  1127. //
  1128. // - 1st item's scaled flex factor: 0.3 * 100px = 30
  1129. // - 2nd item's scaled flex factor: 0.1 * 200px = 20
  1130. // - 1st item's share of distributed free space: 30/(30+20) = 60%
  1131. // - 2nd item's share of distributed free space: 20/(30+20) = 40%
  1132. //
  1133. // SO:
  1134. // - 1st item gets 60% * -40px = -24px. 100px-24px = 76px
  1135. // - 2nd item gets 40% * -40px = -16px. 200px-16px = 184px
  1136. },
  1137. // ...now with min-size modifying how much one item can shrink:
  1138. {
  1139. items:
  1140. [
  1141. {
  1142. "flex": "0 0.3 100px",
  1143. "_main-size": [ null, "70px" ]
  1144. },
  1145. {
  1146. "flex": "0 0.1 200px",
  1147. "_min-main-size": "190px",
  1148. "_main-size": [ null, "190px" ]
  1149. }
  1150. ]
  1151. // Notes:
  1152. // - We proceed as in previous testcase, but clamp the second flex item
  1153. // at its min main size.
  1154. // - After that point, we have a total flex-shrink of = 0.3, so we
  1155. // distribute 0.3 * -100px = -30px to the remaining unfrozen flex
  1156. // items. Since there's only one unfrozen item left, it gets all of it.
  1157. },
  1158. // ...now with min-size larger than our flex-basis:
  1159. // (This makes us freeze the second item right away, before we compute
  1160. // the initial free space.)
  1161. {
  1162. items:
  1163. [
  1164. {
  1165. "flex": "0 0.3 100px",
  1166. "_main-size": [ null, "55px" ] // +30% of 200px-100px-250px
  1167. },
  1168. {
  1169. "flex": "0 0.1 200px",
  1170. "_min-main-size": "250px",
  1171. "_main-size": [ null, "250px" ] // immediately frozen
  1172. }
  1173. ]
  1174. // (Same as previous example, except the min-main-size prevents the
  1175. // second item from shrinking at all)
  1176. },
  1177. // ...and now with a min-size and a small flex-basis, such that we initially
  1178. // have positive free space, which makes the "% of [original] free space"
  1179. // calculations a bit more subtle. We set the "original free space" after
  1180. // we've clamped the second item (the first time the free space is negative).
  1181. {
  1182. items:
  1183. [
  1184. {
  1185. "flex": "0 0.3 100px",
  1186. "_main-size": [ null, "70px" ]
  1187. },
  1188. {
  1189. "flex": "0 0.1 50px",
  1190. "_min-main-size": "200px",
  1191. "_main-size": [ null, "200px" ]
  1192. }
  1193. ]
  1194. },
  1195. // Now with max-size making an item shrink more than its flex-shrink value
  1196. // calls for:
  1197. {
  1198. items:
  1199. [
  1200. {
  1201. "flex": "0 0.3 100px",
  1202. "_main-size": [ null, "70px" ]
  1203. },
  1204. {
  1205. "flex": "0 0.1 200px",
  1206. "_max-main-size": "150px",
  1207. "_main-size": [ null, "150px" ]
  1208. }
  1209. ]
  1210. // Notes:
  1211. // - We proceed as in an earlier testcase, but clamp the second flex item
  1212. // at its max main size.
  1213. // - After that point, we have a total flex-shrink of = 0.3, so we
  1214. // distribute 0.3 * -100px = -30px to the remaining unfrozen flex
  1215. // items. Since there's only one unfrozen item left, it gets all of it.
  1216. },
  1217. // ...and now with a small enough max-size that it prevents the other flex
  1218. // item from taking its full desired portion of the (negative) original free
  1219. // space:
  1220. {
  1221. items:
  1222. [
  1223. {
  1224. "flex": "0 0.3 100px",
  1225. "_main-size": [ null, "90px" ]
  1226. },
  1227. {
  1228. "flex": "0 0.1 200px",
  1229. "_max-main-size": "110px",
  1230. "_main-size": [ null, "110px" ]
  1231. }
  1232. ]
  1233. // Notes:
  1234. // - We proceed as in an earlier testcase, but clamp the second flex item
  1235. // at its max main size.
  1236. // - After that point, we have a total flex-shrink of 0.3, which would
  1237. // have us distribute 0.3 * -100px = -30px to the (one) remaining
  1238. // unfrozen flex item. But our remaining free space is only -10px at
  1239. // that point, so we distribute that instead.
  1240. },
  1241. // ...and now with a small enough max-size that it pushes the other flex item
  1242. // to actually grow a bit (with custom "flex-grow: 1" for this testcase):
  1243. {
  1244. items:
  1245. [
  1246. {
  1247. "flex": "1 0.3 100px",
  1248. "_main-size": [ null, "120px" ]
  1249. },
  1250. {
  1251. "flex": "1 0.1 200px",
  1252. "_max-main-size": "80px",
  1253. "_main-size": [ null, "80px" ]
  1254. }
  1255. ]
  1256. },
  1257. // In this case, the items' flexibilities don't initially sum to < 1, but they
  1258. // do after we freeze the third item for violating its min-size.
  1259. {
  1260. items:
  1261. [
  1262. {
  1263. "flex": "0 0.3 100px",
  1264. "_main-size": [ null, "76px" ]
  1265. },
  1266. {
  1267. "flex": "0 0.1 150px",
  1268. "_main-size": [ null, "138px" ]
  1269. },
  1270. {
  1271. "flex": "0 0.8 10px",
  1272. "_min-main-size": "40px",
  1273. "_main-size": [ null, "40px" ]
  1274. }
  1275. ]
  1276. // Notes:
  1277. // - We immediately freeze the 3rd item, since we're shrinking and its
  1278. // min size obviously prevents it from shrinking at all. This leaves
  1279. // 200px - 100px - 150px - 40px = -90px of "initial free space".
  1280. //
  1281. // - Our remaining flexible items have a total flex-shrink of 0.4,
  1282. // so we can distribute a total of 0.4 * -90px = -36px
  1283. //
  1284. // - We distribute that space using *scaled* flex factors:
  1285. // * 1st item's scaled flex factor: 0.3 * 100px = 30
  1286. // * 2nd item's scaled flex factor: 0.1 * 150px = 15
  1287. // ...which means...
  1288. // * 1st item's share of distributed free space: 30/(30+15) = 2/3
  1289. // * 2nd item's share of distributed free space: 15/(30+15) = 1/3
  1290. //
  1291. // SO:
  1292. // - 1st item gets 2/3 * -36px = -24px. 100px - 24px = 76px
  1293. // - 2nd item gets 1/3 * -36px = -12px. 150px - 12px = 138px
  1294. },
  1295. // In this case, the items' flexibilities sum to > 1, in part due to an item
  1296. // that *can't actually shrink* due to its 0 flex-basis (which gives it a
  1297. // "scaled flex factor" of 0). This prevents us from triggering the special
  1298. // behavior for flexibilities that sum to less than 1, and as a result, the
  1299. // first item ends up absorbing all of the free space.
  1300. {
  1301. items:
  1302. [
  1303. {
  1304. "flex": "0 .5 300px",
  1305. "_main-size": [ null, "200px" ]
  1306. },
  1307. {
  1308. "flex": "0 5 0px",
  1309. "_main-size": [ null, "0px" ]
  1310. }
  1311. ]
  1312. },
  1313. // This case is similar to the one above, but with a *barely* nonzero base
  1314. // size for the second item. This should produce a result similar to the case
  1315. // above. (In particular, we should first distribute a very small amount of
  1316. // negative free space to the second item, getting it to approximately zero,
  1317. // and distribute the bulk of the negative free space to the first item,
  1318. // getting it to approximately 200px.)
  1319. {
  1320. items:
  1321. [
  1322. {
  1323. "flex": "0 .5 300px",
  1324. "_main-size": [ null, "200px" ]
  1325. },
  1326. {
  1327. "flex": "0 1 0.01px",
  1328. "_main-size": [ null, "0px" ]
  1329. }
  1330. ]
  1331. },
  1332. // This case is similar to the ones above, but now we've increased the
  1333. // flex-shrink value on the second-item so that it claims enough of the
  1334. // negative free space to go below its min-size (0px). So, it triggers a min
  1335. // violation & is frozen. For the loop *after* the min violation, the sum of
  1336. // the remaining flex items' flex-shrink values is less than 1, so we trigger
  1337. // the special <1 behavior and only distribute half of the remaining
  1338. // (negative) free space to the first item (instead of all of it).
  1339. {
  1340. items:
  1341. [
  1342. {
  1343. "flex": "0 .5 300px",
  1344. "_main-size": [ null, "250px" ]
  1345. },
  1346. {
  1347. "flex": "0 5 0.01px",
  1348. "_main-size": [ null, "0px" ]
  1349. }
  1350. ]
  1351. },
  1352. ];