1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
|
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<style>
.container {
display: flex;
background-color: grey;
font: 14px sans-serif;
height: 50px;
}
#flex-sanity {
/* This just needs to be large enough so that no shrinking is required. */
width: 1600px;
}
.clamped-huge-item {
flex: 1 1 1650px; /* This needs to be bigger than #flex-sanity, to test
the scenario it's aiming to test (early clamping of a
flex item which, if left unclamped, would've reversed
the direction of flexing & made everything shrink). */
max-width: 10px;
}
.base { align-self: baseline; }
.lastbase { align-self: last baseline; }
.offset { margin-top: 10px;
margin-bottom: 3px; }
.lime { background: lime; }
.yellow { background: yellow; }
.orange { background: orange; }
.pink { background: pink; }
.tan { background: tan; }
.white { background: white; }
.crossMinMax { min-height: 40px;
max-height: 120px; }
.mainMinMax { min-width: 120px;
max-width: 500px; }
.flexGrow { flex-grow: 1; }
.spacer150 { width: 150px;
box-sizing: border-box;
height: 10px;
border: 1px solid teal; }
</style>
<script>
"use strict";
SimpleTest.waitForExplicitFinish();
const TEXT_NODE = Node.TEXT_NODE;
function testItemMatchesExpectedValues(item, values, index) {
if (typeof(values.node) != "undefined") {
is(item.node, values.node, "Item index " + index + " has expected node.");
}
if (typeof(values.mainBaseSize) != "undefined") {
is(item.mainBaseSize, values.mainBaseSize, "Item index " + index + " has expected mainBaseSize.");
}
if (typeof(values.mainDeltaSize) != "undefined") {
is(item.mainDeltaSize, values.mainDeltaSize, "Item index " + index + " has expected mainDeltaSize.");
}
if (typeof(values.mainMinSize) != "undefined") {
is(item.mainMinSize, values.mainMinSize, "Item index " + index + " has expected mainMinSize.");
}
if (typeof(values.mainMaxSize) != "undefined") {
is(item.mainMaxSize, values.mainMaxSize, "Item index " + index + " has expected mainMaxSize.");
} else {
// If we didn't specify an expected mainMaxSize, then it's implied
// that the max main-size property (max-width/max-height) is at its
// default "none" value, which our FlexItem API represents as +infinity.
is(item.mainMaxSize, Number.POSITIVE_INFINITY,
"Item index " + index + " has expected (default) mainMaxSize.");
}
if (typeof(values.crossMinSize) != "undefined") {
is(item.crossMinSize, values.crossMinSize, "Item index " + index + " has expected crossMinSize.");
}
if (typeof(values.crossMaxSize) != "undefined") {
is(item.crossMaxSize, values.crossMaxSize, "Item index " + index + " has expected crossMaxSize.");
} else {
// As above for mainMaxSize, no-expected-value implies we expect +infinity.
is(item.crossMaxSize, Number.POSITIVE_INFINITY,
"Item index " + index + " has expected (default) crossMaxSize.");
}
}
// Test for items in "flex-sanity" flex container:
function testFlexSanity() {
let container = document.getElementById("flex-sanity");
let flex = container.getAsFlexContainer();
let lines = flex.getLines();
is(lines.length, 1, "Container should have expected number of lines.");
let line = lines[0];
let containerHeight = container.getBoundingClientRect().height;
is(line.crossSize, containerHeight,
"Line crossSize should equal the height of the container.");
// We can't compare baselines precisely, so we'll just confirm that they
// appear somewhere within the elements that determine them.
// (This assumes the first rect is baseline-aligned.)
let firstRect = container.firstElementChild.getBoundingClientRect();
ok(line.firstBaselineOffset > firstRect.top &&
line.firstBaselineOffset < firstRect.bottom,
"Line firstBaselineOffset should land somewhere within the element " +
"that determines it.");
// For last baseline, it's measured from the bottom, so we have to compare
// against the element bounds subtracted from the container height.
// We use the first node which uses last-baseline ("lb") alignment to
// provide the rect:
let lbElem = document.querySelector(".lastbase");
let lbElemBoundingRect = lbElem.getBoundingClientRect();
ok(line.lastBaselineOffset > containerHeight - lbElemBoundingRect.bottom &&
line.lastBaselineOffset < containerHeight - lbElemBoundingRect.top,
"Line lastBaselineOffset should land somewhere within the element" +
"that determines it.");
let expectedValues = [
{ crossMinSize: 0 },
{ mainBaseSize: 100,
mainDeltaSize: 0 },
{ crossMinSize: 40,
crossMaxSize: 120,
mainDeltaSize: 0 },
{ mainMinSize: 120,
mainMaxSize: 500,
mainDeltaSize: 0 },
{ mainMinSize: 120,
mainMaxSize: 500,
mainBaseSize: 150,
mainDeltaSize: 0 },
{ mainBaseSize: 10,
mainMaxSize: 5,
mainDeltaSize: 0 },
{ mainBaseSize: 10,
mainMinSize: 15,
mainDeltaSize: 0 },
{ mainBaseSize: 50,
mainMaxSize: 10 },
{ mainBaseSize: 1650,
mainMaxSize: 10,
mainDeltaSize: 0 },
{ mainDeltaSize: 0 },
{ /* final item is anonymous flex item */ },
];
let items = line.getItems();
is(items.length, expectedValues.length,
"Line should have expected number of items.");
is(items.length, container.children.length + 1,
"Line should have as many items as the flex container has child elems, " +
"plus 1 for anonymous flex item");
for (let i = 0; i < items.length; ++i) {
let item = items[i];
let values = expectedValues[i];
// set the expected node to the node we're currently iterating over,
// except for:
// - the display:contents element (whose item is its first child)
// - the final item (which is an anonymous flex item around text)
if (i < container.children.length) {
let curElem = container.children[i];
values.node = (curElem.style.display == "contents"
? curElem.firstElementChild
: curElem);
} else {
is(container.lastChild.nodeType, TEXT_NODE,
"container's last child should be a text node");
values.node = container.lastChild;
}
testItemMatchesExpectedValues(item, values, i);
}
// Check that the delta size of the first item is (roughly) equal to the
// actual size minus the base size.
isfuzzy(items[0].mainDeltaSize, firstRect.width - items[0].mainBaseSize, 1e-4,
"flex-grow item should have expected mainDeltaSize.");
}
// Test for items in "flex-growing" flex container:
function testFlexGrowing() {
let expectedValues = [
{ mainBaseSize: 10,
mainDeltaSize: 10,
mainMinSize: 35 },
{ mainBaseSize: 20,
mainDeltaSize: 5,
mainMinSize: 28 },
{ mainBaseSize: 30,
mainDeltaSize: 7 },
{ mainBaseSize: 0,
mainDeltaSize: 48,
mainMaxSize: 20 },
];
let container = document.getElementById("flex-growing");
let items = container.getAsFlexContainer().getLines()[0].getItems();
is(items.length, container.children.length,
"Line should have as many items as the flex container has child elems");
for (let i = 0; i < items.length; ++i) {
let item = items[i];
let values = expectedValues[i];
testItemMatchesExpectedValues(item, values, i);
}
}
function runTests() {
testFlexSanity();
testFlexGrowing();
SimpleTest.finish();
}
</script>
</head>
<body onLoad="runTests();">
<!-- First flex container to be tested: "flex-sanity".
We test a few general things, e.g.:
- accuracy of reported baselines.
- accuracy of reported flex base size, min/max sizes, & trivial deltas.
- flex items formation for display:contents subtrees & text nodes.
-->
<div id="flex-sanity" class="container">
<div class="lime base flexGrow">one line (first)</div>
<div class="yellow lastbase" style="width: 100px">one line (last)</div>
<div class="orange offset lastbase crossMinMax">two<br/>lines and offset (last)</div>
<div class="pink offset base mainMinMax">offset (first)</div>
<!-- Inflexible item w/ content-derived flex base size, which has min/max
but doesn't violate them: -->
<div class="tan mainMinMax">
<div class="spacer150"></div>
</div>
<!-- Inflexible item that is trivially clamped to smaller max-width: -->
<div style="flex: 0 0 10px; max-width: 5px"></div>
<!-- Inflexible item that is trivially clamped to larger min-width: -->
<div style="flex: 0 0 10px; min-width: 15px"></div>
<!-- Item that wants to grow but is trivially clamped to max-width
below base size: -->
<div style="flex: 1 1 50px; max-width: 10px"></div>
<div class="clamped-huge-item"></div>
<div style="display:contents">
<div class="white">replaced</div>
</div>
anon item for text
</div>
<!-- Second flex container to be tested, with items that grow by specific
predictable amounts. This ends up triggering 4 passes of the main
flexbox layout algorithm loop - and note that for each item, we only
report (via 'mainDeltaSize') the delta that it wanted in the *last pass
of the loop before that item was frozen*.
Here's what goes on in each pass (and the tentative deltas)
* PASS 1
- Available space = 120 - 10 - 20 - 30 - 0 = 60px.
- Sum of flex values = 1+1+2+16 = 20. So 1 "share" is 60px/20 = 3px.
- Deltas (space distributed): +3px, +3px, +6px, +48px
VIOLATIONS:
item0 is now 10+3 = 13px, which under its min
item1 is now 20+3 = 23px, which under its min
item3 is now 0+48 = 48px, which over its max
- the magnitude of max-violations (how far we're out of bounds) exceeds
magnitude of min-violations, so we prioritize the max-violations.
- So we freeze item3 at its max-width, 20px, leaving its final "desired"
mainDeltaSize at +48px from this pass.
* PASS 2
- Available space = 120 - 10 - 20 - 30 - 20 = 40px.
- Sum of flex values = 1+1+2 = 4. So 1 "share" is 40px/4 = 10px.
- Deltas (space distributed): +10px, +10px, +20px, +0px.
VIOLATIONS:
item0 is now 10+10 = 20px, which is under its min
- So we freeze item0 at its min-width, 35px, leaving its final "desired"
mainDeltaSize at +10px from this pass.
* PASS 3
- Available space = 120 - 35 - 20 - 30 - 20 = 15px.
- Sum of flex values = 1+2 = 3. So 1 "share" is 15px/3 = 5px.
- Deltas (space distributed): +0px, +5px, +10px, +0px.
VIOLATIONS:
item1 is now 20+5 = 25px, which is under its min
- So we freeze item1 at its min-width, 28px, leaving its final "desired"
mainDeltaSize at +5px from this pass.
* PASS 4
- Available space = 120 - 35 - 28 - 30 - 20 = 7px.
- Sum of flex values = 2. So 1 "share" is 7px/2 = 3.5px
- Deltas (space distributed): +0px, +0px, +7px, +0px.
VIOLATIONS:
None! (So, we'll be done after this pass!)
- So we freeze item2 (the only unfrozen item) at its flexed size, 37px,
and set its final "desired" mainDeltaSize to +7px from this pass.
- And we're done!
-->
<div id="flex-growing" class="container" style="width: 120px">
<div style="flex: 1 10px; min-width: 35px"></div>
<div style="flex: 1 20px; min-width: 28px"></div>
<div style="flex: 2 30px; min-width: 0"></div>
<div style="flex: 16 0px; max-width: 20px"></div>
</div>
</body>
</html>
|