@react-x11/components 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/code-editor/node.d.ts +13 -0
  2. package/dist/code-editor/node.d.ts.map +1 -1
  3. package/dist/code-editor/node.js +23 -5
  4. package/dist/code-editor/node.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +209 -23
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +873 -175
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +25 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +307 -19
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/content.d.ts +11 -6
  14. package/dist/html/css/content.d.ts.map +1 -1
  15. package/dist/html/css/content.js +171 -12
  16. package/dist/html/css/content.js.map +1 -1
  17. package/dist/html/css/parse.d.ts.map +1 -1
  18. package/dist/html/css/parse.js +62 -9
  19. package/dist/html/css/parse.js.map +1 -1
  20. package/dist/html/css/shapes.d.ts +62 -0
  21. package/dist/html/css/shapes.d.ts.map +1 -0
  22. package/dist/html/css/shapes.js +214 -0
  23. package/dist/html/css/shapes.js.map +1 -0
  24. package/dist/html/css/style.d.ts +42 -5
  25. package/dist/html/css/style.d.ts.map +1 -1
  26. package/dist/html/css/style.js +75 -27
  27. package/dist/html/css/style.js.map +1 -1
  28. package/dist/html/css/ua.js +15 -0
  29. package/dist/html/css/ua.js.map +1 -1
  30. package/dist/html/css/values.d.ts +5 -0
  31. package/dist/html/css/values.d.ts.map +1 -1
  32. package/dist/html/css/values.js +18 -9
  33. package/dist/html/css/values.js.map +1 -1
  34. package/dist/html/index.d.ts.map +1 -1
  35. package/dist/html/index.js +7 -0
  36. package/dist/html/index.js.map +1 -1
  37. package/dist/html/layout/block.d.ts +38 -7
  38. package/dist/html/layout/block.d.ts.map +1 -1
  39. package/dist/html/layout/block.js +160 -50
  40. package/dist/html/layout/block.js.map +1 -1
  41. package/dist/html/layout/boxes.d.ts +29 -2
  42. package/dist/html/layout/boxes.d.ts.map +1 -1
  43. package/dist/html/layout/boxes.js +159 -20
  44. package/dist/html/layout/boxes.js.map +1 -1
  45. package/dist/html/layout/css-grid.d.ts.map +1 -1
  46. package/dist/html/layout/css-grid.js +69 -19
  47. package/dist/html/layout/css-grid.js.map +1 -1
  48. package/dist/html/layout/flex.d.ts.map +1 -1
  49. package/dist/html/layout/flex.js +1648 -225
  50. package/dist/html/layout/flex.js.map +1 -1
  51. package/dist/html/layout/inline.d.ts +15 -3
  52. package/dist/html/layout/inline.d.ts.map +1 -1
  53. package/dist/html/layout/inline.js +23 -8
  54. package/dist/html/layout/inline.js.map +1 -1
  55. package/dist/html/layout/table.d.ts +27 -1
  56. package/dist/html/layout/table.d.ts.map +1 -1
  57. package/dist/html/layout/table.js +907 -323
  58. package/dist/html/layout/table.js.map +1 -1
  59. package/dist/html/node.d.ts +36 -4
  60. package/dist/html/node.d.ts.map +1 -1
  61. package/dist/html/node.js +174 -37
  62. package/dist/html/node.js.map +1 -1
  63. package/dist/html/paint.d.ts +4 -1
  64. package/dist/html/paint.d.ts.map +1 -1
  65. package/dist/html/paint.js +769 -204
  66. package/dist/html/paint.js.map +1 -1
  67. package/dist/html/resources.js +14 -1
  68. package/dist/html/resources.js.map +1 -1
  69. package/dist/html/svg.d.ts +50 -17
  70. package/dist/html/svg.d.ts.map +1 -1
  71. package/dist/html/svg.js +501 -83
  72. package/dist/html/svg.js.map +1 -1
  73. package/dist/html/widgets.d.ts.map +1 -1
  74. package/dist/html/widgets.js +57 -4
  75. package/dist/html/widgets.js.map +1 -1
  76. package/package.json +3 -3
  77. package/src/code-editor/node.ts +24 -5
  78. package/src/html/css/cascade.ts +987 -176
  79. package/src/html/css/color.ts +345 -18
  80. package/src/html/css/content.ts +173 -15
  81. package/src/html/css/parse.ts +60 -8
  82. package/src/html/css/shapes.ts +273 -0
  83. package/src/html/css/style.ts +123 -30
  84. package/src/html/css/ua.ts +15 -0
  85. package/src/html/css/values.ts +23 -9
  86. package/src/html/index.ts +7 -0
  87. package/src/html/layout/block.ts +169 -56
  88. package/src/html/layout/boxes.ts +198 -24
  89. package/src/html/layout/css-grid.ts +89 -22
  90. package/src/html/layout/flex.ts +1863 -219
  91. package/src/html/layout/inline.ts +27 -7
  92. package/src/html/layout/table.ts +989 -333
  93. package/src/html/node.ts +204 -37
  94. package/src/html/paint.ts +999 -252
  95. package/src/html/resources.ts +13 -1
  96. package/src/html/svg.ts +600 -78
  97. package/src/html/widgets.ts +61 -4
@@ -11,15 +11,15 @@
11
11
  // inline pass ask "how wide is the line at this y" without knowing whose
12
12
  // formatting context it is inside.
13
13
  import { AUTO, gapOf, isPct, isTransparent, resolve, resolveOrNull, } from '../css/values.js';
14
- import { CONTAIN_INLINE_SIZE, CONTAIN_LAYOUT, CONTAIN_PAINT, CONTAIN_SIZE, scrolls, } from '../css/style.js';
14
+ import { CONTAIN_INLINE_SIZE, CONTAIN_LAYOUT, CONTAIN_PAINT, CONTAIN_SIZE, INLINE_BEFORE_ABSOLUTE, scrolls, } from '../css/style.js';
15
15
  import { about, linearOf, matrixOf, transformed } from '../css/transform.js';
16
16
  import { lineOf, spanToName } from './grid-lines.js';
17
17
  import { columnsOf, forgetColumns, layoutColumns, spansColumns, } from './multicol.js';
18
18
  import { BOX_RAISES, Box, CLAMPED, CLEARED_FROM, COLUMN_PIECES, CUT_BLOCKS, GRID_TRACKS, FIRST_LINE, INLINE_OFFSETS, MOVED_OFF_LINES, LINE_BOX_RAISES, SHIFTED_LINES, TEXT_SHIFTS, columned, isBlank, } from './boxes.js';
19
19
  import { FloatContext } from './floats.js';
20
- import { faceExtentOf, isButton, joinsBefore, layoutInline, lineHeightMultiplier, spaceBreaksOnly, strutOf, widestWord, } from './inline.js';
20
+ import { emptyLinePoint, faceExtentOf, isButton, joinsBefore, layoutInline, lineHeightMultiplier, spaceBreaksOnly, strutOf, widestWord, } from './inline.js';
21
21
  import { layoutFlex } from './flex.js';
22
- import { finishCaptions, layoutTable } from './table.js';
22
+ import { finishCaptions, givenHeight, layoutTable } from './table.js';
23
23
  import { collapseEdges } from './collapse.js';
24
24
  import { clipsFor, computePaintBounds, FIXED_BOXES, fixedToViewport, hoistNegative, OUT_OF_FLOW_REACH, stackLayers, } from '../paint.js';
25
25
  /** Lay the whole document out at a width. */
@@ -622,7 +622,16 @@ function layoutChildren(box, ctx, floats, contentTop, contentWidth, leading = 0)
622
622
  marked = null;
623
623
  }
624
624
  if (child.outOfFlow) {
625
- placeStatic(child, box, contentLeft, contentWidth, y + marginOf(pendingMargin));
625
+ const top = y + marginOf(pendingMargin);
626
+ if (INLINE_BEFORE_ABSOLUTE.has(child.style)) {
627
+ // one that was inline-level is on a line of its own, beside the
628
+ // floats there, where the line's alignment puts what it holds
629
+ const band = floats.bandAt(top, 1, contentLeft, contentLeft + contentWidth);
630
+ const at = emptyLinePoint(box.style, band.left, band.right);
631
+ placeStatic(child, box, at, 0, top);
632
+ }
633
+ else
634
+ placeStatic(child, box, contentLeft, contentWidth, top);
626
635
  ctx.positioned.push({
627
636
  box: child,
628
637
  containing: containingBlockFor(child) ?? box,
@@ -1327,38 +1336,61 @@ export const MIN_CONTENT_PROBE = 0;
1327
1336
  * a cell set to 10% measured no wider than its padding.
1328
1337
  */
1329
1338
  export function measureIntrinsicWidth(box, ctx, available, content = false) {
1330
- ctx.layoutSubtree(box, available);
1339
+ // not through `ctx.layoutSubtree`, which is this and one frame more on
1340
+ // the stack for every level of a document that measures what it holds
1341
+ // (`layoutTable`)
1342
+ layoutSubtree(box, ctx, available, undefined, true);
1331
1343
  const specified = box.style.width;
1332
1344
  if (!content && specified !== AUTO && Number.isFinite(box.width)) {
1333
1345
  return box.width;
1334
1346
  }
1335
- return intrinsicWidth(box) + box.horizontalExtra;
1347
+ return (intrinsicWidth(box, undefined, available === MIN_CONTENT_PROBE) +
1348
+ box.horizontalExtra);
1336
1349
  }
1337
1350
  /**
1338
1351
  * Lay a box and everything under it out at a width, at the origin — what
1339
- * `LayoutContext.layoutSubtree` hands to `flex.ts` and `table.ts`, and what
1340
- * the shrink-to-fit probe uses. A replaced box is sized rather than laid out,
1341
- * because there is nothing inside it to lay out.
1352
+ * `LayoutContext.layoutSubtree` hands to `flex.ts` and `css-grid.ts`, and
1353
+ * what the shrink-to-fit probe and a table's cells use. A replaced box is
1354
+ * sized rather than laid out, because there is nothing inside it to lay out.
1342
1355
  */
1343
- function layoutSubtree(box, ctx, width,
1356
+ export function layoutSubtree(box, ctx, width,
1344
1357
  /** Its containing block's width, where `width` is the box's own. */
1345
- containing = width) {
1346
- resolveEdges(box, Number.isFinite(containing) ? containing : 0);
1358
+ containing,
1359
+ /** Whether this lays it out to measure it (`measureIntrinsicWidth`). */
1360
+ measuring = false) {
1361
+ const base = containing ?? width;
1362
+ resolveEdges(box, Number.isFinite(base) ? base : 0);
1347
1363
  if (box.kind === 'replaced') {
1348
1364
  sizeReplaced(box, width);
1349
1365
  box.x = 0;
1350
1366
  box.y = 0;
1351
1367
  return;
1352
1368
  }
1353
- // within its limits, a percentage among them of its containing block's
1369
+ // Within its limits, a percentage among them of its containing block's
1354
1370
  // width and not of its own: a flex item `max-width: 50%` that the flex
1355
- // layout made half its row was laid out at a quarter of it. A table
1356
- // cell's is of nothing, and ignored: what a percentage limit does to a
1357
- // cell it does where its column is sized (CSS Tables 3, 3.8.2), and a
1358
- // cell is laid out in the width its columns came to.
1359
- const borderBox = Number.isFinite(width)
1360
- ? clampWidth(box, width, box.kind === 'table-cell' ? NaN : containing)
1361
- : Infinity;
1371
+ // layout made half its row was laid out at a quarter of it. An
1372
+ // intrinsic limit is among them, as it is in a block's flow: without it,
1373
+ // a flex or grid item `min-width: max-content` that its layout made as
1374
+ // wide as its content was laid out at a smaller `max-width`, where the
1375
+ // minimum wins (CSS Sizing 3, 3.1). Not where the box is laid out to be
1376
+ // measured, since that is what an intrinsic limit is measured from.
1377
+ //
1378
+ // A table cell laid out in its columns, which hand it its row's width,
1379
+ // is as wide as they are (CSS Tables 3, 3.10.2): its limits are theirs
1380
+ // to weigh (3.8.2), and have been. Clamped again, `td { max-width:
1381
+ // 50px }` in a column 200 wide set its text in 50 of it. Probed for
1382
+ // what it asks of them, at none, it keeps within its length limits as
1383
+ // any box does; a percentage one is of nothing and does not hold it.
1384
+ let borderBox = Infinity;
1385
+ if (Number.isFinite(width)) {
1386
+ if (box.kind !== 'table-cell') {
1387
+ borderBox = clampWidth(box, width, base, measuring ? undefined : ctx);
1388
+ }
1389
+ else if (containing !== undefined)
1390
+ borderBox = width;
1391
+ else
1392
+ borderBox = clampWidth(box, width, NaN);
1393
+ }
1362
1394
  layoutInternals(box, ctx, borderBox, 0, 0);
1363
1395
  }
1364
1396
  /**
@@ -1704,12 +1736,12 @@ function layoutMarker(box, marker, ctx) {
1704
1736
  const style = box.style;
1705
1737
  if (marker.image) {
1706
1738
  // an image: its bottom on the first line's baseline, as an inline
1707
- // image's is, and the gap a bullet has before the content
1739
+ // image's is, and its own gap before the content (`MARKER_IMAGE_GAP`)
1708
1740
  const first = firstLineIn(box);
1709
1741
  marker.y = first
1710
1742
  ? first.y + first.baseline - marker.image.height
1711
1743
  : box.contentY;
1712
- const gap = Math.round(style.fontSize * 0.4);
1744
+ const { gap } = marker.image;
1713
1745
  // at the start of the line, which is its right in a right-to-left item
1714
1746
  marker.x =
1715
1747
  style.direction === 'rtl'
@@ -1743,8 +1775,13 @@ function layoutMarker(box, marker, ctx) {
1743
1775
  });
1744
1776
  marker.layout = layout;
1745
1777
  // a space between it and the content — the one its style's suffix ends
1746
- // in, or its own text's — in its own face: a line measures without it
1747
- const gap = marker.flush && !/\s$/.test(marker.text) ? 0 : markerGap(fonts, face);
1778
+ // in, or its own text's — in its own face: a line measures without the
1779
+ // spaces it ends in, and with a no-break space, which `content: "1.\a0"`
1780
+ // writes and which was a second gap on top of its own
1781
+ const trailing = marker.flush
1782
+ ? (/[ \t\n\r\f]*$/.exec(marker.text)?.[0].length ?? 0)
1783
+ : 1;
1784
+ const gap = trailing ? trailing * markerGap(fonts, face) : 0;
1748
1785
  // The marker sits on the first line of the item's *content*, which is not
1749
1786
  // always the item's own: an `<li>` holding a paragraph, or one holding text
1750
1787
  // and a nested list, has its inline content in an anonymous block. Looking
@@ -1903,19 +1940,33 @@ function stretchHeight(box) {
1903
1940
  * stretched item's, a flexed one's in a container of a definite height (CSS
1904
1941
  * Flexbox 9.8), a grid item's stretched down its area (CSS Grid 1, 11.1) —
1905
1942
  * which what is in it takes its percentages of, and which an item that is
1906
- * a flex or grid container itself lays its own items out in. Set by
1907
- * `flex.ts` and `css-grid.ts` for the item's own layout alone.
1943
+ * a flex or grid container itself lays its own items out in, and a table
1944
+ * shares out among its rows. Set by `flex.ts` and `css-grid.ts` for the
1945
+ * item's own layout alone.
1908
1946
  */
1909
1947
  export const FLEXED_HEIGHT = new WeakMap();
1910
1948
  /**
1911
1949
  * The content height a flex layout gave an item without making it definite:
1912
1950
  * flexed along a column that has no height of its own, which 9.8 leaves
1913
1951
  * indefinite. An item that is a flex or grid container lays its own items
1914
- * out in it all the same — it is its used size (9.7) — and nothing in it
1915
- * takes a percentage of it. Set by `flex.ts` for the item's own layout
1916
- * alone.
1952
+ * out in it all the same — it is its used size (9.7) — as a table shares
1953
+ * it among its rows, and nothing in it takes a percentage of it. Set by
1954
+ * `flex.ts` for the item's own layout alone.
1917
1955
  */
1918
1956
  export const USED_HEIGHT = new WeakMap();
1957
+ /**
1958
+ * A table a grid or a flex layout gives its width, whose `auto` width is
1959
+ * then the one it is laid out at. `normal` stretches any grid item but a
1960
+ * replaced one across its area (CSS Grid 1, 6.2), and a flex item across a
1961
+ * column's line (CSS Flexbox 9.4, step 11), a table among them; and a flex
1962
+ * item along a row is the size the line flexed it to (9.7) — where in a
1963
+ * block's flow a table's `auto` width is its columns' (CSS 2.1 17.5.2;
1964
+ * Blink leaves tables out of the boxes a block stretches, `space_utils.cc`,
1965
+ * and stretches them in a grid, `grid_item.cc`, and a flex box). Set by
1966
+ * `css-grid.ts` and `flex.ts` for the item's own layout alone, and never
1967
+ * while its widths are measured.
1968
+ */
1969
+ export const STRETCHED_ACROSS = new WeakSet();
1919
1970
  /**
1920
1971
  * Hand a box's children the height their percentages resolve against —
1921
1972
  * through inline boxes, which are no containing block, to the inline-block
@@ -2081,6 +2132,14 @@ function finishHeight(box, contentHeight) {
2081
2132
  if (box.kind === 'table-cell') {
2082
2133
  CELL_CONTENT.set(box, contentHeight + box.verticalExtra);
2083
2134
  }
2135
+ // A table a flex or grid layout gave a height is as tall as that, or as
2136
+ // its rows where they need more, which `layoutTable` has shared it out
2137
+ // among — whatever height it has of its own, which the flex layout
2138
+ // started from (`layoutTable`)
2139
+ if (box.kind === 'table' && Number.isFinite(givenHeight(box))) {
2140
+ box.height = contentHeight + box.verticalExtra;
2141
+ return;
2142
+ }
2084
2143
  const set = specifiedHeight(box);
2085
2144
  // at least zero: a `calc()` may come to less
2086
2145
  const specified = set === null ? null : Math.max(0, set);
@@ -2135,7 +2194,9 @@ export function clampHeight(box, height) {
2135
2194
  out = Math.min(out, max + (box.style.boxSizing === 'border-box' ? 0 : box.verticalExtra));
2136
2195
  if (min !== null)
2137
2196
  out = Math.max(out, min + (box.style.boxSizing === 'border-box' ? 0 : box.verticalExtra));
2138
- return Math.max(0, out);
2197
+ // no shorter than its padding and borders, as `clampWidth` is no
2198
+ // narrower
2199
+ return Math.max(box.verticalExtra, out);
2139
2200
  }
2140
2201
  /** Place a block-level box horizontally, honouring `margin: auto`. */
2141
2202
  function placeBlock(box, contentLeft, y, containingWidth) {
@@ -2324,7 +2385,10 @@ room = containingWidth) {
2324
2385
  if (style.minWidthKeyword && ctx) {
2325
2386
  out = Math.max(out, keywordWidth(box, ctx, style.minWidthKeyword, containingWidth, room));
2326
2387
  }
2327
- return Math.max(0, out);
2388
+ // and no narrower than its padding and borders: a `border-box` length
2389
+ // less than them leaves its content box none wide, and not less (CSS
2390
+ // Sizing 3, 3.3)
2391
+ return Math.max(box.horizontalExtra, out);
2328
2392
  }
2329
2393
  /** An intrinsic `min-width` or `max-width` in the room a box's margins
2330
2394
  * leave it. Apart from `clampWidth`, which every block is sized through,
@@ -2354,21 +2418,25 @@ export function contentSizedWidth(box, ctx, size, available, percentBase) {
2354
2418
  // the content's sizes, whatever width the box has of its own: a
2355
2419
  // `min-width: max-content` beside a `width` is its content's widest,
2356
2420
  // and a probe of the box at no width answered the probe's width
2357
- let probed = false;
2358
2421
  if (box.intrinsicMaxContent < 0) {
2359
2422
  box.intrinsicMaxContent = measureIntrinsicWidth(box, ctx, Infinity, true);
2360
- probed = true;
2361
2423
  }
2362
2424
  if (size !== 'max-content' && box.intrinsicMinContent < 0) {
2363
2425
  box.intrinsicMinContent = measureIntrinsicWidth(box, ctx, MIN_CONTENT_PROBE, true);
2364
- probed = true;
2365
2426
  }
2366
- if (probed)
2367
- resolveEdges(box, percentBase);
2368
- const max = box.intrinsicMaxContent;
2427
+ // The probes resolved a percentage of the box's padding or border
2428
+ // against nothing, which is how the sizes are kept, whatever the width
2429
+ // around the box: what one comes to against `percentBase` goes on top.
2430
+ // Left out, `width: max-content; padding-left: 10%` was as wide as its
2431
+ // content, the padding inside it.
2432
+ resolveEdges(box, 0);
2433
+ const bare = box.horizontalExtra;
2434
+ resolveEdges(box, Number.isFinite(percentBase) ? percentBase : 0);
2435
+ const extra = box.horizontalExtra - bare;
2436
+ const max = box.intrinsicMaxContent + extra;
2369
2437
  if (size === 'max-content')
2370
2438
  return max;
2371
- const min = box.intrinsicMinContent;
2439
+ const min = box.intrinsicMinContent + extra;
2372
2440
  if (size === 'min-content')
2373
2441
  return min;
2374
2442
  return Math.min(max, Math.max(min, fitRoom(box, size, available, percentBase)));
@@ -2495,6 +2563,11 @@ const PREFERRED = new WeakMap();
2495
2563
  * or is indented, a block with widths of its own.
2496
2564
  */
2497
2565
  export function exactMinContent(box, fonts) {
2566
+ // a flex row's is its items' side by side, a grid's its columns', and a
2567
+ // replaced element has no words: read as its widest word, a row of two
2568
+ // in a flex column's item was as narrow as the wider of them
2569
+ if (box.kind !== 'block' && box.kind !== 'table-cell')
2570
+ return null;
2498
2571
  if (hasWidths(box.style) || contained(box, CONTAIN_WIDTH))
2499
2572
  return null;
2500
2573
  const inner = exactWords(box, fonts);
@@ -2632,13 +2705,28 @@ function wordBound(box, fonts, measured) {
2632
2705
  * `seen.cut` is set where a line it measured was cut short by
2633
2706
  * `text-overflow`, in a box with a width to cut it at: the line is wider
2634
2707
  * than it was drawn, and the answer is of what was drawn.
2708
+ *
2709
+ * `narrowest` says the box was laid out by the min-content probe
2710
+ * (`MIN_CONTENT_PROBE`), and the answer is its min-content width: the
2711
+ * probe lays a box out at no width, but one with a least width of its own
2712
+ * at that, and nothing in the layout tells the two apart.
2635
2713
  */
2636
- export function intrinsicWidth(box, seen) {
2714
+ export function intrinsicWidth(box, seen, narrowest = false) {
2637
2715
  // under size containment, as though it held nothing: the size
2638
2716
  // `contain-intrinsic-size` gives it, or none (CSS Containment 2, 3.2)
2639
2717
  if (contained(box, CONTAIN_WIDTH)) {
2640
2718
  return box.style.containIntrinsicWidth ?? 0;
2641
2719
  }
2720
+ // a table is as wide as its layout made it: its columns, the spacing
2721
+ // either side of them and between, and its captions' least (`layoutTable`)
2722
+ // — at no width limit its widest, and at none its narrowest. What it
2723
+ // holds says less: its rows run from the first column to the last, the
2724
+ // spacing either side outside them, and measured by them a table in a
2725
+ // flex row, a grid, a float or an `inline-table` came to that much too
2726
+ // narrow, and its columns gave it up
2727
+ if (box.kind === 'table' && Number.isFinite(box.width)) {
2728
+ return Math.max(0, box.width - box.horizontalExtra);
2729
+ }
2642
2730
  // a grid laid out at no width limit has its columns at their widest, and
2643
2731
  // is as wide as they are — an item that runs past its column makes it no
2644
2732
  // wider — or, where it has none, as where its items end
@@ -2656,10 +2744,16 @@ export function intrinsicWidth(box, seen) {
2656
2744
  }
2657
2745
  let widest = 0;
2658
2746
  // A row of flex items is as wide as all of them side by side, and gaps
2659
- // between them; any other box is as wide as the widest thing in it
2747
+ // between them; any other box is as wide as the widest thing in it. So
2748
+ // is a row that wraps, at its narrowest: each of its items may be a line
2749
+ // of its own, and it is as wide as the widest of them (CSS Flexbox
2750
+ // 9.9.1). Summed, a wrapping row of three 30px items made a
2751
+ // `width: min-content` box around it 90 wide, the items side by side,
2752
+ // where Chrome has 30, one under another
2660
2753
  const display = box.style.display;
2661
2754
  const row = (display === 'flex' || display === 'inline-flex') &&
2662
2755
  box.style.flexDirection.startsWith('row');
2756
+ const wraps = row && narrowest && box.style.flexWrap !== 'nowrap';
2663
2757
  let total = 0;
2664
2758
  let items = 0;
2665
2759
  const lines = box.lines;
@@ -2728,7 +2822,7 @@ export function intrinsicWidth(box, seen) {
2728
2822
  }
2729
2823
  }
2730
2824
  else {
2731
- let inner = intrinsicWidth(child, seen) + child.horizontalExtra;
2825
+ let inner = intrinsicWidth(child, seen, narrowest) + child.horizontalExtra;
2732
2826
  if (typeof style.maxWidth === 'number') {
2733
2827
  inner = Math.min(inner, style.maxWidth + contentExtra(child));
2734
2828
  }
@@ -2741,11 +2835,11 @@ export function intrinsicWidth(box, seen) {
2741
2835
  across = Math.max(across, contribution);
2742
2836
  continue;
2743
2837
  }
2744
- if (row) {
2838
+ if (row && !wraps) {
2745
2839
  total += contribution;
2746
2840
  items += 1;
2747
2841
  }
2748
- else if (!sideBySide)
2842
+ else if (wraps || !sideBySide)
2749
2843
  widest = Math.max(widest, contribution);
2750
2844
  else if (child.isFloat) {
2751
2845
  const clear = style.clear;
@@ -2865,7 +2959,17 @@ stretchRoom = containingWidth) {
2865
2959
  const ih = own.missing & 2 ? null : own.height;
2866
2960
  // `aspect-ratio` over its own, unless written `auto` and it has one
2867
2961
  const aspect = style.aspectRatio;
2868
- const ratio = aspect && !(aspect.auto && own.ratio > 0) ? aspect.ratio : own.ratio;
2962
+ const pages = aspect !== null && !(aspect.auto && own.ratio > 0);
2963
+ const ratio = pages ? aspect.ratio : own.ratio;
2964
+ // A ratio of the page's is of the box `box-sizing` names (CSS Sizing 4,
2965
+ // 5.1): one axis's content size to the other's goes through the border
2966
+ // box there. Written with `auto`, it is of the content box, as the
2967
+ // replaced element's own is, whichever it is.
2968
+ const outer = pages && !aspect.auto && style.boxSizing === 'border-box';
2969
+ const ox = outer ? box.horizontalExtra : 0;
2970
+ const oy = outer ? box.verticalExtra : 0;
2971
+ const widthOf = (h) => Math.max(0, (h + oy) * ratio - ox);
2972
+ const heightOf = (w) => Math.max(0, (w + ox) / ratio - oy);
2869
2973
  // the width a block would have here: what a ratio with no size fills, and
2870
2974
  // an `hr`, whose whole appearance is its border across the line
2871
2975
  const room = () => {
@@ -2879,18 +2983,18 @@ stretchRoom = containingWidth) {
2879
2983
  // which moves both axes together
2880
2984
  if (iw !== null) {
2881
2985
  w = iw;
2882
- h = ih ?? iw / ratio;
2986
+ h = ih ?? heightOf(iw);
2883
2987
  }
2884
2988
  else if (ih !== null) {
2885
2989
  h = ih;
2886
- w = ih * ratio;
2990
+ w = widthOf(ih);
2887
2991
  }
2888
2992
  else {
2889
2993
  // a ratio and no size: CSS 2.1 leaves it undefined and suggests the
2890
2994
  // width a block would have, which is what browsers do — or, where the
2891
2995
  // containing block waits on this box, the default object size
2892
2996
  w = Number.isFinite(containingWidth) ? room() : own.width;
2893
- h = w / ratio;
2997
+ h = heightOf(w);
2894
2998
  }
2895
2999
  [w, h] = constrained(w, h, minW, maxW, minH, maxH);
2896
3000
  }
@@ -2902,7 +3006,7 @@ stretchRoom = containingWidth) {
2902
3006
  if (width !== null)
2903
3007
  w = width;
2904
3008
  else if (usedHeight !== null && ratio > 0)
2905
- w = usedHeight * ratio;
3009
+ w = widthOf(usedHeight);
2906
3010
  else if (box.replaced === 'hr')
2907
3011
  w = room();
2908
3012
  else
@@ -2911,7 +3015,7 @@ stretchRoom = containingWidth) {
2911
3015
  if (usedHeight !== null)
2912
3016
  h = usedHeight;
2913
3017
  else if (ratio > 0)
2914
- h = clamp(w / ratio, minH, maxH);
3018
+ h = clamp(heightOf(w), minH, maxH);
2915
3019
  else
2916
3020
  h = clamp(own.height, minH, maxH);
2917
3021
  }
@@ -3192,9 +3296,15 @@ function layoutPositioned(box, containing, ctx) {
3192
3296
  let y;
3193
3297
  if (top !== null && bottom !== null) {
3194
3298
  const fill = Math.max(0, cbHeight - top - bottom - box.marginTop - box.marginBottom);
3195
- // — a box with a ratio has its height from its width
3299
+ // — a box with a ratio has its height from its width, and a table is
3300
+ // as tall as its rows, as it is as wide as its columns between two
3301
+ // offsets: `normal` stretches neither (Blink: "Replaced/tables don't
3302
+ // stretch in abspos", `out_of_flow_layout_part.cc`), and a table taken
3303
+ // to the height its offsets leave was that tall around rows that were
3304
+ // not
3196
3305
  const stretches = style.height === AUTO &&
3197
3306
  (box.kind !== 'replaced' || control) &&
3307
+ box.kind !== 'table' &&
3198
3308
  !boxRatio(box);
3199
3309
  // both offsets and no height: the box fills what they leave, its
3200
3310
  // `auto` margins nothing (10.6.4, rule 5) — unless `min-height` or