From 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:47:29 +0200 Subject: Adding upstream version 115.8.0esr. Signed-off-by: Daniel Baumann --- layout/generic/AnonymousContentKey.h | 54 + layout/generic/AspectRatio.cpp | 32 + layout/generic/AspectRatio.h | 147 + layout/generic/AutoCopyListener.h | 60 + layout/generic/BRFrame.cpp | 269 + layout/generic/BlockReflowState.cpp | 1090 + layout/generic/BlockReflowState.h | 417 + layout/generic/CSSAlignUtils.cpp | 129 + layout/generic/CSSAlignUtils.h | 65 + layout/generic/CSSOrderAwareFrameIterator.cpp | 88 + layout/generic/CSSOrderAwareFrameIterator.h | 268 + layout/generic/ColumnSetWrapperFrame.cpp | 315 + layout/generic/ColumnSetWrapperFrame.h | 88 + layout/generic/ColumnUtils.cpp | 47 + layout/generic/ColumnUtils.h | 39 + layout/generic/FrameClass.py | 28 + layout/generic/FrameClasses.py | 164 + layout/generic/GenerateFrameLists.py | 41 + layout/generic/JustificationUtils.h | 127 + layout/generic/LayoutMessageUtils.h | 54 + layout/generic/MathMLTextRunFactory.cpp | 679 + layout/generic/MathMLTextRunFactory.h | 44 + layout/generic/MiddleCroppingBlockFrame.cpp | 206 + layout/generic/MiddleCroppingBlockFrame.h | 62 + layout/generic/PrintedSheetFrame.cpp | 307 + layout/generic/PrintedSheetFrame.h | 123 + layout/generic/ReflowInput.cpp | 3045 + layout/generic/ReflowInput.h | 1062 + layout/generic/ReflowOutput.cpp | 90 + layout/generic/ReflowOutput.h | 280 + layout/generic/RubyUtils.cpp | 185 + layout/generic/RubyUtils.h | 223 + layout/generic/ScrollAnchorContainer.cpp | 796 + layout/generic/ScrollAnchorContainer.h | 183 + layout/generic/ScrollAnimationBezierPhysics.cpp | 152 + layout/generic/ScrollAnimationBezierPhysics.h | 94 + layout/generic/ScrollAnimationMSDPhysics.cpp | 122 + layout/generic/ScrollAnimationMSDPhysics.h | 60 + layout/generic/ScrollAnimationPhysics.h | 60 + layout/generic/ScrollGeneration.cpp | 49 + layout/generic/ScrollGeneration.h | 70 + layout/generic/ScrollOrigin.h | 66 + layout/generic/ScrollPositionUpdate.cpp | 140 + layout/generic/ScrollPositionUpdate.h | 130 + layout/generic/ScrollSnap.cpp | 633 + layout/generic/ScrollSnap.h | 94 + layout/generic/ScrollSnapTargetId.h | 41 + layout/generic/ScrollVelocityQueue.cpp | 93 + layout/generic/ScrollVelocityQueue.h | 93 + layout/generic/ScrollbarActivity.cpp | 431 + layout/generic/ScrollbarActivity.h | 164 + layout/generic/ScrollbarPreferences.h | 22 + layout/generic/StickyScrollContainer.cpp | 414 + layout/generic/StickyScrollContainer.h | 115 + layout/generic/TextDrawTarget.h | 614 + layout/generic/TextOverflow.cpp | 935 + layout/generic/TextOverflow.h | 332 + layout/generic/ViewportFrame.cpp | 499 + layout/generic/ViewportFrame.h | 118 + layout/generic/Visibility.h | 45 + layout/generic/WBRFrame.cpp | 68 + layout/generic/WritingModes.h | 2236 + layout/generic/broken-image.png | Bin 0 -> 160 bytes layout/generic/crashtests/1001233.html | 18 + layout/generic/crashtests/1001258-1.html | 26 + layout/generic/crashtests/1001994.html | 14 + layout/generic/crashtests/1003441.xhtml | 37 + layout/generic/crashtests/1015562.html | 15 + layout/generic/crashtests/1015563-1.html | 4 + layout/generic/crashtests/1015563-2.html | 7 + layout/generic/crashtests/1015844.html | 25 + layout/generic/crashtests/1032450.html | 12 + layout/generic/crashtests/1032613-1.svg | 10 + layout/generic/crashtests/1032613-2.html | 17 + layout/generic/crashtests/1037903.html | 6 + layout/generic/crashtests/1039454-1.html | 12 + layout/generic/crashtests/1042489.html | 6 + layout/generic/crashtests/1054010-1.html | 97 + layout/generic/crashtests/1058954-1.html | 13 + layout/generic/crashtests/1059138-1.html | 38 + layout/generic/crashtests/1102175-2.html | 47 + layout/generic/crashtests/1134531.html | 4 + layout/generic/crashtests/1134667.html | 2 + layout/generic/crashtests/1137723-1.html | 29 + layout/generic/crashtests/1137723-2.html | 29 + layout/generic/crashtests/1140043-1.html | 7 + layout/generic/crashtests/1140043-2.html | 7 + layout/generic/crashtests/1140043-3.html | 7 + layout/generic/crashtests/1140268-1.html | 18 + layout/generic/crashtests/1145768.html | 21 + layout/generic/crashtests/1145931.html | 16 + layout/generic/crashtests/1146103.html | 6 + layout/generic/crashtests/1146107.html | 6 + layout/generic/crashtests/1146114.html | 6 + layout/generic/crashtests/1153478-iframe.html | 18 + layout/generic/crashtests/1153478.html | 11 + layout/generic/crashtests/1153695.html | 25 + layout/generic/crashtests/1156222.html | 6 + layout/generic/crashtests/1156257.html | 16 + layout/generic/crashtests/1157011.html | 4 + layout/generic/crashtests/1169420-1.html | 8 + layout/generic/crashtests/1169420-2.html | 8 + layout/generic/crashtests/1178783-1.html | 157 + layout/generic/crashtests/1183431.html | 6 + layout/generic/crashtests/1186147-1.html | 6 + layout/generic/crashtests/1209952.html | 38 + layout/generic/crashtests/1221112-1.html | 32 + layout/generic/crashtests/1221112-2.html | 27 + layout/generic/crashtests/1221874-1.html | 16 + layout/generic/crashtests/1221904.html | 24 + layout/generic/crashtests/1222783.xhtml | 19 + layout/generic/crashtests/1223522.xhtml | 5 + layout/generic/crashtests/1223568-1.html | 2 + layout/generic/crashtests/1223568-2.html | 6 + layout/generic/crashtests/1224230-1.html | 22 + layout/generic/crashtests/1225005.html | 4 + layout/generic/crashtests/1225118.html | 4 + layout/generic/crashtests/1225376.html | 10 + layout/generic/crashtests/1225592.html | 13 + layout/generic/crashtests/1229437-1.html | 8 + layout/generic/crashtests/1229437-2.html | 5 + layout/generic/crashtests/1230378.xhtml | 16 + layout/generic/crashtests/1233191.html | 9 + layout/generic/crashtests/1233607.html | 9 + layout/generic/crashtests/1234701-1.html | 19 + layout/generic/crashtests/1234701-2.html | 18 + layout/generic/crashtests/1248227.html | 13 + layout/generic/crashtests/1271765.html | 8 + layout/generic/crashtests/1272983-1.html | 15 + layout/generic/crashtests/1272983-2.html | 15 + layout/generic/crashtests/1275059.html | 3 + layout/generic/crashtests/1278007.html | 26 + layout/generic/crashtests/1278080.html | 29 + layout/generic/crashtests/1278461-1.html | 23 + layout/generic/crashtests/1278461-2.html | 22 + layout/generic/crashtests/1279814.html | 35 + layout/generic/crashtests/1281102.html | 30 + .../crashtests/1297427-non-equal-centers.html | 14 + layout/generic/crashtests/1304441.html | 9 + layout/generic/crashtests/1308876-1.html | 38 + layout/generic/crashtests/1316649.html | 54 + layout/generic/crashtests/1316884-1.html | 21 + layout/generic/crashtests/1343552-1.html | 32 + layout/generic/crashtests/1343552-2.html | 31 + layout/generic/crashtests/1346454-1.html | 34 + layout/generic/crashtests/1346454-2.html | 12 + layout/generic/crashtests/1349650.html | 11 + layout/generic/crashtests/1349816-1.html | 6 + layout/generic/crashtests/1350372.html | 31 + layout/generic/crashtests/1364361-1.html | 45 + layout/generic/crashtests/1367413-1.html | 54 + layout/generic/crashtests/1368617-1.html | 9 + layout/generic/crashtests/1373586.html | 19 + layout/generic/crashtests/1375858.html | 12 + layout/generic/crashtests/1381134-2.html | 45 + layout/generic/crashtests/1381134.html | 45 + layout/generic/crashtests/1401420-1.html | 5 + layout/generic/crashtests/1401709.html | 10 + layout/generic/crashtests/1401807.html | 15 + layout/generic/crashtests/1404222-empty-shape.html | 2 + layout/generic/crashtests/1405443.html | 19 + layout/generic/crashtests/1405813.html | 10 + layout/generic/crashtests/1405896.html | 31 + layout/generic/crashtests/1406252-1.html | 13 + layout/generic/crashtests/1415185.html | 18 + layout/generic/crashtests/1416544.html | 32 + layout/generic/crashtests/1427824.html | 10 + layout/generic/crashtests/1431781-2.html | 26 + layout/generic/crashtests/1431781.html | 22 + layout/generic/crashtests/1458028.html | 13 + layout/generic/crashtests/1459697.html | 13 + layout/generic/crashtests/1460158-1.html | 15 + layout/generic/crashtests/1460158-2.html | 17 + layout/generic/crashtests/1460158-3.html | 14 + layout/generic/crashtests/1461039.html | 15 + layout/generic/crashtests/1461979-1.html | 13 + layout/generic/crashtests/1463977.html | 5 + layout/generic/crashtests/1466224.html | 27 + layout/generic/crashtests/1467239.html | 14 + layout/generic/crashtests/1472403.html | 6 + layout/generic/crashtests/1474768.html | 22 + layout/generic/crashtests/1478178.html | 6 + layout/generic/crashtests/1483972.html | 10 + layout/generic/crashtests/1486457.html | 19 + layout/generic/crashtests/1488762-1.html | 16 + layout/generic/crashtests/1488910-1.html | 19 + layout/generic/crashtests/1488910-2.html | 19 + layout/generic/crashtests/1489287.html | 17 + layout/generic/crashtests/1489770.html | 23 + layout/generic/crashtests/1489863.html | 15 + layout/generic/crashtests/1490032.html | 20 + layout/generic/crashtests/1490685.html | 1 + layout/generic/crashtests/1493708.html | 15 + layout/generic/crashtests/1493710.html | 26 + layout/generic/crashtests/1493741.html | 6 + layout/generic/crashtests/1494380.html | 10 + layout/generic/crashtests/1505817.html | 27 + layout/generic/crashtests/1506216.html | 4 + layout/generic/crashtests/1506306.html | 8 + layout/generic/crashtests/1507196.html | 30 + layout/generic/crashtests/1513275.html | 9 + layout/generic/crashtests/1513282.html | 16 + layout/generic/crashtests/1515124.html | 26 + layout/generic/crashtests/1517033.html | 24 + layout/generic/crashtests/1517297.html | 22 + layout/generic/crashtests/1520798-1.xhtml | 10 + layout/generic/crashtests/1520798-2.html | 13 + layout/generic/crashtests/1528771.html | 10 + layout/generic/crashtests/1539656.html | 14 + layout/generic/crashtests/1542441.html | 37 + layout/generic/crashtests/1543140-1.html | 24 + layout/generic/crashtests/1544060-1.html | 3 + layout/generic/crashtests/1544060-2.html | 1 + layout/generic/crashtests/1553824.html | 86 + layout/generic/crashtests/1554824.html | 25 + layout/generic/crashtests/1555142.html | 15 + layout/generic/crashtests/1560349.html | 12 + layout/generic/crashtests/1560397-2.html | 25 + layout/generic/crashtests/1560397.html | 21 + layout/generic/crashtests/1562105.html | 6 + layout/generic/crashtests/1563131.html | 17 + layout/generic/crashtests/1568001-1.html | 19 + layout/generic/crashtests/1568001-2.html | 13 + layout/generic/crashtests/1569639.html | 13 + layout/generic/crashtests/1571239.html | 22 + layout/generic/crashtests/1571460.html | 29 + layout/generic/crashtests/1571598.html | 14 + layout/generic/crashtests/1571897.html | 15 + layout/generic/crashtests/1572901.html | 19 + layout/generic/crashtests/1573216.html | 20 + layout/generic/crashtests/1574552.html | 6 + layout/generic/crashtests/1574993.html | 21 + layout/generic/crashtests/1582019.html | 22 + layout/generic/crashtests/1586470.html | 9 + .../crashtests/1588955-very-large-frameset.html | 9 + layout/generic/crashtests/1590569.html | 24 + layout/generic/crashtests/1596310.html | 17 + layout/generic/crashtests/1601819-1.html | 11 + layout/generic/crashtests/1608851-1.html | 12 + layout/generic/crashtests/1608851-2.html | 12 + layout/generic/crashtests/1613210.html | 31 + layout/generic/crashtests/1614101.html | 44 + layout/generic/crashtests/1618312.html | 19 + layout/generic/crashtests/1618564.html | 12 + layout/generic/crashtests/1625051-1.html | 13 + layout/generic/crashtests/1625051-2.html | 13 + layout/generic/crashtests/1626970.html | 14 + layout/generic/crashtests/1628804.html | 21 + layout/generic/crashtests/1629575-1.html | 21 + layout/generic/crashtests/1629575-2.html | 69 + layout/generic/crashtests/1630385.html | 26 + layout/generic/crashtests/1633434.html | 15 + layout/generic/crashtests/1633737-1.html | 8 + layout/generic/crashtests/1633737-2.html | 8 + layout/generic/crashtests/1633737-3.html | 34 + layout/generic/crashtests/1633737-4.html | 53 + layout/generic/crashtests/1633737-5.html | 39 + layout/generic/crashtests/1633828.html | 36 + layout/generic/crashtests/1638860-1.html | 24 + layout/generic/crashtests/1638860-2.html | 26 + layout/generic/crashtests/1638906.html | 25 + layout/generic/crashtests/1640028.html | 31 + layout/generic/crashtests/1640051.html | 20 + layout/generic/crashtests/1640275.html | 14 + layout/generic/crashtests/1644819.html | 20 + layout/generic/crashtests/1645549-1.html | 19 + layout/generic/crashtests/1648577.html | 18 + layout/generic/crashtests/1652618.html | 15 + layout/generic/crashtests/1652897.html | 13 + layout/generic/crashtests/1654925.html | 16 + layout/generic/crashtests/1663222.html | 19 + layout/generic/crashtests/1666592.html | 2 + layout/generic/crashtests/1670336.html | 33 + layout/generic/crashtests/1676970.html | 20 + layout/generic/crashtests/1677518-1.html | 15 + layout/generic/crashtests/1677518-1.jpg | Bin 0 -> 1295 bytes layout/generic/crashtests/1677518-1.svg | 29 + layout/generic/crashtests/1679794.html | 27 + layout/generic/crashtests/1680406.html | 16 + layout/generic/crashtests/1681788.html | 13 + layout/generic/crashtests/1682032.html | 17 + layout/generic/crashtests/1682686-1.html | 60 + layout/generic/crashtests/1682686-2.html | 35 + layout/generic/crashtests/1682882.html | 27 + layout/generic/crashtests/1683126.html | 15 + layout/generic/crashtests/1697262-1.html | 17 + layout/generic/crashtests/1699263.html | 18 + layout/generic/crashtests/1699468.html | 30 + layout/generic/crashtests/1728319.html | 11 + layout/generic/crashtests/1730506.html | 21 + layout/generic/crashtests/1730570.html | 33 + layout/generic/crashtests/1734015.html | 21 + layout/generic/crashtests/1776079.html | 19 + layout/generic/crashtests/1791606.html | 29 + layout/generic/crashtests/1799749.html | 5 + layout/generic/crashtests/1807958.html | 33 + layout/generic/crashtests/1816574.html | 39 + layout/generic/crashtests/1821603.html | 4 + layout/generic/crashtests/1822118.html | 11 + layout/generic/crashtests/1825434.html | 16 + layout/generic/crashtests/225868-1-inner.html | 14 + layout/generic/crashtests/225868-1.html | 7 + layout/generic/crashtests/255468.xhtml | 24 + layout/generic/crashtests/255982-1.html | 13 + layout/generic/crashtests/255982-2.html | 10 + layout/generic/crashtests/255982-3.html | 10 + layout/generic/crashtests/255982-4.html | 13 + layout/generic/crashtests/25888-1.html | 6 + layout/generic/crashtests/25888-2.html | 8 + layout/generic/crashtests/264937-1.html | 18 + layout/generic/crashtests/265867-1.html | 11 + layout/generic/crashtests/265867-2.html | 3 + layout/generic/crashtests/286491.html | 26 + layout/generic/crashtests/289864-1.html | 5 + layout/generic/crashtests/289864-1.jpg | Bin 0 -> 42186 bytes layout/generic/crashtests/295292-1.html | 13 + layout/generic/crashtests/295292-2.html | 23 + layout/generic/crashtests/302260-1.html | 21 + layout/generic/crashtests/307979-1.html | 27 + layout/generic/crashtests/309322-1.html | 56 + layout/generic/crashtests/309322-2.html | 56 + layout/generic/crashtests/309322-3.html | 48 + layout/generic/crashtests/309322-4.html | 48 + layout/generic/crashtests/310556-1.xhtml | 21 + layout/generic/crashtests/321224.xhtml | 6 + layout/generic/crashtests/322780-1.xhtml | 6 + layout/generic/crashtests/323381-1.html | 1 + layout/generic/crashtests/323381-2.html | 1 + layout/generic/crashtests/323386-1.html | 1 + layout/generic/crashtests/323389-1.html | 7 + layout/generic/crashtests/323389-2.html | 8 + layout/generic/crashtests/323493-1.html | 16 + layout/generic/crashtests/323495-1.html | 14 + layout/generic/crashtests/324318-1.html | 29 + layout/generic/crashtests/328946-1.html | 1 + layout/generic/crashtests/331284-1.xhtml | 13 + layout/generic/crashtests/331292.html | 258 + layout/generic/crashtests/334105-1.xhtml | 35 + layout/generic/crashtests/334107-1.xhtml | 9 + layout/generic/crashtests/334147-1.xhtml | 16 + layout/generic/crashtests/334148-1.xhtml | 14 + layout/generic/crashtests/334602-1.html | 12 + layout/generic/crashtests/337412-1.html | 29 + layout/generic/crashtests/337883-1.html | 20 + layout/generic/crashtests/337883-2.html | 21 + layout/generic/crashtests/339769-1.html | 22 + layout/generic/crashtests/342322-1.html | 28 + layout/generic/crashtests/343206-1.xhtml | 21 + layout/generic/crashtests/344557-1.html | 32 + layout/generic/crashtests/345139-1.xhtml | 53 + layout/generic/crashtests/345617-1.html | 8 + layout/generic/crashtests/348510-1.html | 7 + layout/generic/crashtests/348510-2.html | 7 + layout/generic/crashtests/348887-1-inner.html | 21 + layout/generic/crashtests/348887-1.html | 9 + layout/generic/crashtests/350370.html | 42 + layout/generic/crashtests/354458-1.html | 10 + layout/generic/crashtests/354458-2.html | 26 + layout/generic/crashtests/355426-1.html | 27 + layout/generic/crashtests/359371-1.html | 66 + layout/generic/crashtests/359371-2.html | 64 + layout/generic/crashtests/360599.html | 25 + layout/generic/crashtests/361109.html | 9 + layout/generic/crashtests/363448.html | 23 + layout/generic/crashtests/363722-1.html | 9 + layout/generic/crashtests/363722-2.html | 10 + layout/generic/crashtests/364220.html | 17 + layout/generic/crashtests/364407-1.html | 44 + layout/generic/crashtests/364686-1.xhtml | 12 + layout/generic/crashtests/366021-1.xhtml | 24 + layout/generic/crashtests/366667-1.html | 6 + layout/generic/crashtests/366952-1.html | 17 + layout/generic/crashtests/367246-1.html | 9 + layout/generic/crashtests/367360.html | 30 + layout/generic/crashtests/368330-1.html | 15 + layout/generic/crashtests/368461-1.xhtml | 11 + layout/generic/crashtests/368568.html | 14 + layout/generic/crashtests/368752.html | 20 + layout/generic/crashtests/368860-1.html | 12 + layout/generic/crashtests/368863-1.html | 5 + layout/generic/crashtests/369150-1.html | 22 + layout/generic/crashtests/369150-2.html | 22 + layout/generic/crashtests/369227-1.xhtml | 19 + layout/generic/crashtests/369542-1.html | 7 + layout/generic/crashtests/369542-2.html | 15 + layout/generic/crashtests/369547-1.html | 16 + layout/generic/crashtests/370174-1.html | 566 + layout/generic/crashtests/370174-2.html | 13 + layout/generic/crashtests/370174-3.html | 25 + layout/generic/crashtests/370699-1.html | 14 + layout/generic/crashtests/370794-1.html | 12 + layout/generic/crashtests/370884-1.xhtml | 14 + layout/generic/crashtests/371348-1.xhtml | 41 + layout/generic/crashtests/371561-1.html | 8 + layout/generic/crashtests/371566-1.xhtml | 13 + layout/generic/crashtests/372376-1.xhtml | 39 + layout/generic/crashtests/373859-1.html | 16 + layout/generic/crashtests/373868-1.xhtml | 19 + layout/generic/crashtests/375462-1.html | 781 + layout/generic/crashtests/375831.html | 11 + layout/generic/crashtests/376419.html | 28 + layout/generic/crashtests/377522.html | 18 + layout/generic/crashtests/37757-1.html | 1 + layout/generic/crashtests/379217-1.xhtml | 7 + layout/generic/crashtests/379217-2.xhtml | 10 + layout/generic/crashtests/379917-1.xhtml | 35 + layout/generic/crashtests/380012-1.html | 42 + layout/generic/crashtests/381152-1.html | 11 + layout/generic/crashtests/382129-1.xhtml | 7 + layout/generic/crashtests/382131-1.html | 25 + layout/generic/crashtests/382199-1.html | 8 + layout/generic/crashtests/382208-1.xhtml | 7 + layout/generic/crashtests/382262-1.html | 10 + layout/generic/crashtests/382396-1.xhtml | 7 + layout/generic/crashtests/383089-1.html | 85 + layout/generic/crashtests/385265-1.xhtml | 13 + layout/generic/crashtests/385295-1.xhtml | 5 + layout/generic/crashtests/385344-1.html | 12 + layout/generic/crashtests/385344-2.html | 10 + layout/generic/crashtests/385414-1.html | 5 + layout/generic/crashtests/385414-2.html | 5 + layout/generic/crashtests/385426-1.html | 5 + layout/generic/crashtests/385526.html | 116 + layout/generic/crashtests/385681.html | 34 + layout/generic/crashtests/386799-1.html | 7 + layout/generic/crashtests/386807-1.html | 19 + layout/generic/crashtests/386812-1.html | 23 + layout/generic/crashtests/386827-1.html | 16 + layout/generic/crashtests/387058-1.html | 16 + layout/generic/crashtests/387058-2.html | 17 + layout/generic/crashtests/387088-1.html | 5 + layout/generic/crashtests/387209-1.html | 6 + layout/generic/crashtests/387213-1.html | 10 + layout/generic/crashtests/387215-1.xhtml | 15 + layout/generic/crashtests/387219-1.xhtml | 8 + layout/generic/crashtests/387233-1.html | 21 + layout/generic/crashtests/387233-2.html | 18 + layout/generic/crashtests/387282-1.html | 7 + layout/generic/crashtests/388049.html | 43 + layout/generic/crashtests/388175-1.html | 24 + layout/generic/crashtests/388367-1.html | 7 + layout/generic/crashtests/388709-1.html | 15 + layout/generic/crashtests/389635-1.html | 14 + layout/generic/crashtests/390050-1.html | 48 + layout/generic/crashtests/390050-2.html | 22 + layout/generic/crashtests/390050-3.html | 4 + layout/generic/crashtests/390052.html | 13 + layout/generic/crashtests/390417.html | 17 + layout/generic/crashtests/390762-1.html | 30 + layout/generic/crashtests/391053-1.xhtml | 16 + layout/generic/crashtests/391894-1.html | 17 + layout/generic/crashtests/392698-1.html | 16 + layout/generic/crashtests/393758-1.xhtml | 10 + layout/generic/crashtests/393906-1.html | 12 + layout/generic/crashtests/393923-1.html | 15 + layout/generic/crashtests/393956-1.html | 25 + layout/generic/crashtests/393956-2.html | 26 + layout/generic/crashtests/393956-3.html | 11 + layout/generic/crashtests/393956-4.html | 11 + layout/generic/crashtests/394237-1.html | 38 + layout/generic/crashtests/394818-1.html | 13 + layout/generic/crashtests/394818-2.html | 16 + layout/generic/crashtests/394820-1.html | 19 + layout/generic/crashtests/395316-1.html | 13 + layout/generic/crashtests/395450-1.xhtml | 28 + layout/generic/crashtests/397007-1.html | 37 + layout/generic/crashtests/397187-1.html | 32 + layout/generic/crashtests/397844-1.xhtml | 55 + layout/generic/crashtests/397844-2.xhtml | 55 + layout/generic/crashtests/397852-1.xhtml | 7 + layout/generic/crashtests/398181-1.html | 10 + layout/generic/crashtests/398181-2.html | 11 + layout/generic/crashtests/398322-1.html | 17 + layout/generic/crashtests/398322-2.html | 12 + layout/generic/crashtests/398332-1.html | 19 + layout/generic/crashtests/398332-2.html | 27 + layout/generic/crashtests/398332-3.html | 4 + layout/generic/crashtests/399407-1.xhtml | 25 + layout/generic/crashtests/399412-1.html | 32 + layout/generic/crashtests/399843-1.html | 64 + layout/generic/crashtests/400078-1.html | 20 + layout/generic/crashtests/400190.html | 63 + layout/generic/crashtests/400223-1.html | 24 + layout/generic/crashtests/400232-1.html | 11 + layout/generic/crashtests/400244-1.html | 31 + layout/generic/crashtests/400768-1.xhtml | 9 + layout/generic/crashtests/400768-2.xhtml | 7 + layout/generic/crashtests/401042-2.html | 5 + layout/generic/crashtests/402380-1.html | 13 + layout/generic/crashtests/402380-2.html | 18 + layout/generic/crashtests/402872-1.html | 3 + layout/generic/crashtests/402872-2.html | 2 + layout/generic/crashtests/403004.html | 3 + layout/generic/crashtests/403143-1.html | 19 + layout/generic/crashtests/403576-1.html | 5 + layout/generic/crashtests/404140-1.html | 7 + layout/generic/crashtests/404146-1.html | 28 + layout/generic/crashtests/404204-1.html | 7 + layout/generic/crashtests/404215-1.html | 29 + layout/generic/crashtests/404215-2.html | 37 + layout/generic/crashtests/404215-3.html | 32 + layout/generic/crashtests/404219-1.html | 30 + layout/generic/crashtests/404219-2.html | 31 + layout/generic/crashtests/404624.html | 7 + layout/generic/crashtests/406137.html | 16 + layout/generic/crashtests/406380.html | 12 + layout/generic/crashtests/406902-1.html | 47 + layout/generic/crashtests/407009-1.xhtml | 7 + layout/generic/crashtests/408304-1.xhtml | 5 + layout/generic/crashtests/408602-1.html | 12 + layout/generic/crashtests/408737-1.html | 14 + layout/generic/crashtests/408737-2.html | 14 + layout/generic/crashtests/408749-1.xhtml | 1 + layout/generic/crashtests/408883-1.html | 39 + layout/generic/crashtests/410198.html | 8 + layout/generic/crashtests/410228-1.html | 7 + layout/generic/crashtests/410232-1.html | 14 + layout/generic/crashtests/410595-1.html | 7 + layout/generic/crashtests/411213-1.html | 9 + layout/generic/crashtests/411213-2.xml | 8 + layout/generic/crashtests/411835.html | 19 + layout/generic/crashtests/411851-1.html | 8 + layout/generic/crashtests/412014-1.html | 17 + layout/generic/crashtests/412201-1.xhtml | 1 + layout/generic/crashtests/412543-1.html | 17 + layout/generic/crashtests/413048-1.html | 9 + layout/generic/crashtests/413079-1.xhtml | 10 + layout/generic/crashtests/413079-2.xhtml | 12 + layout/generic/crashtests/413079-3.xhtml | 12 + layout/generic/crashtests/413085-1.html | 23 + layout/generic/crashtests/413085-2.html | 14 + layout/generic/crashtests/413582-1.xhtml | 9 + layout/generic/crashtests/413582-2.html | 9 + layout/generic/crashtests/413712-1.xhtml | 18 + layout/generic/crashtests/414061-1.html | 12 + layout/generic/crashtests/414180-1.xhtml | 7 + layout/generic/crashtests/414719-1.html | 25 + layout/generic/crashtests/415685-1.html | 14 + layout/generic/crashtests/415818.xhtml | 9 + layout/generic/crashtests/416165.html | 23 + layout/generic/crashtests/416264-1.html | 8 + layout/generic/crashtests/416476-1.html | 2 + layout/generic/crashtests/417848-1.xhtml | 6 + layout/generic/crashtests/417902-1.html | 23 + layout/generic/crashtests/417902-2.html | 28 + layout/generic/crashtests/418532-1.html | 9 + layout/generic/crashtests/418932-1.html | 2 + layout/generic/crashtests/419352.html | 3 + layout/generic/crashtests/420000-1.html | 10 + layout/generic/crashtests/420718.html | 1 + layout/generic/crashtests/421404-1.html | 20 + layout/generic/crashtests/421671.html | 202 + layout/generic/crashtests/422283-1.html | 10 + layout/generic/crashtests/422301-1.html | 24 + layout/generic/crashtests/423055-1.html | 10 + layout/generic/crashtests/423098.html | 22 + layout/generic/crashtests/423264-1.html | 19 + layout/generic/crashtests/424629.html | 21 + layout/generic/crashtests/425253-1.html | 5 + layout/generic/crashtests/426040-1.html | 28 + layout/generic/crashtests/426272-1.html | 18 + layout/generic/crashtests/428263-1.html | 18 + layout/generic/crashtests/429960-1.html | 17 + layout/generic/crashtests/429960-2.html | 18 + layout/generic/crashtests/429969-1.html | 24 + layout/generic/crashtests/429981-1.html | 27 + layout/generic/crashtests/430332-1.html | 17 + layout/generic/crashtests/430344-1.html | 5 + layout/generic/crashtests/430352-1.html | 5 + layout/generic/crashtests/430744-1.html | 10 + layout/generic/crashtests/430991.html | 24 + layout/generic/crashtests/431260-1.html | 34 + layout/generic/crashtests/431260-2.html | 26 + layout/generic/crashtests/435529.html | 20 + layout/generic/crashtests/436194-1.html | 18 + layout/generic/crashtests/436602-1.html | 8 + layout/generic/crashtests/436822-1.html | 22 + layout/generic/crashtests/436823.html | 10 + layout/generic/crashtests/436969-1.html | 10 + layout/generic/crashtests/437156-1.html | 10 + layout/generic/crashtests/437565-1.xhtml | 7 + layout/generic/crashtests/437565-2.xhtml | 24 + layout/generic/crashtests/437565-3.xhtml | 23 + layout/generic/crashtests/438259-1.html | 13 + layout/generic/crashtests/438266-1.html | 33 + layout/generic/crashtests/438509-1.html | 80 + layout/generic/crashtests/443528-1.html | 19 + layout/generic/crashtests/444230-1.html | 1 + layout/generic/crashtests/444484-1.html | 27 + layout/generic/crashtests/444726-1.xhtml | 10 + layout/generic/crashtests/444861-1.html | 18 + layout/generic/crashtests/445288.html | 15 + layout/generic/crashtests/448903-1.html | 5 + layout/generic/crashtests/448996-1.html | 26 + layout/generic/crashtests/451315-1.html | 5 + layout/generic/crashtests/451317-1.html | 24 + layout/generic/crashtests/451334-1.html | 10 + layout/generic/crashtests/452157-1.html | 8 + layout/generic/crashtests/452157-2.html | 39 + layout/generic/crashtests/452157-3.html | 39 + layout/generic/crashtests/453762-1.html | 4 + layout/generic/crashtests/455171-1.html | 5 + layout/generic/crashtests/455171-2.html | 7 + layout/generic/crashtests/455171-3.html | 2 + layout/generic/crashtests/455643-1.xhtml | 19 + layout/generic/crashtests/457375.html | 5 + layout/generic/crashtests/457380-1.html | 26 + layout/generic/crashtests/459968.html | 33 + layout/generic/crashtests/460910-1.xml | 14 + layout/generic/crashtests/461294-1.html | 1 + layout/generic/crashtests/462968.xhtml | 5 + layout/generic/crashtests/463350-1.html | 17 + layout/generic/crashtests/463350-2.html | 17 + layout/generic/crashtests/463350-3.html | 15 + layout/generic/crashtests/463741-1.html | 20 + layout/generic/crashtests/465651-1.html | 45 + layout/generic/crashtests/467137-1.html | 24 + layout/generic/crashtests/467213-1.html | 9 + layout/generic/crashtests/467487-1.html | 11 + layout/generic/crashtests/467493-1.html | 7 + layout/generic/crashtests/467493-2.html | 25 + layout/generic/crashtests/467875-1.xhtml | 10 + layout/generic/crashtests/467914-1.html | 3 + layout/generic/crashtests/468207-1.html | 5 + layout/generic/crashtests/468771-1.xhtml | 27 + layout/generic/crashtests/468771-2.xhtml | 22 + layout/generic/crashtests/469859-1.xhtml | 32 + layout/generic/crashtests/471360.html | 61 + layout/generic/crashtests/472587-1.xhtml | 28 + layout/generic/crashtests/472617-1.xhtml | 4 + layout/generic/crashtests/472774-1.html | 25 + layout/generic/crashtests/472776-1.html | 20 + layout/generic/crashtests/472950-1.html | 21 + layout/generic/crashtests/473278-1.xhtml | 1 + layout/generic/crashtests/473894-1.html | 6 + layout/generic/crashtests/476241-1.html | 2 + layout/generic/crashtests/477731-1.html | 6 + layout/generic/crashtests/477928.html | 18 + layout/generic/crashtests/478131-1.html | 7 + layout/generic/crashtests/478170-1.html | 17 + layout/generic/crashtests/478185-1.html | 61 + layout/generic/crashtests/478504.html | 33 + layout/generic/crashtests/479938-1.html | 23 + layout/generic/crashtests/480345-1.html | 5 + layout/generic/crashtests/481921-iframe.html | 12 + layout/generic/crashtests/481921.html | 20 + layout/generic/crashtests/481921.ogg | Bin 0 -> 42852 bytes layout/generic/crashtests/489462-1.html | 21 + layout/generic/crashtests/489477.html | 21 + layout/generic/crashtests/489480-1.xhtml | 1 + layout/generic/crashtests/489647-1.html | 13 + layout/generic/crashtests/493111-1.html | 22 + layout/generic/crashtests/493118-1.html | 6 + layout/generic/crashtests/493649.html | 5 + layout/generic/crashtests/494283-1.xhtml | 4 + layout/generic/crashtests/494283-2.html | 6 + layout/generic/crashtests/494332-1.html | 7 + layout/generic/crashtests/495875-1.html | 7 + layout/generic/crashtests/495875-2.html | 7 + layout/generic/crashtests/496742.html | 11 + layout/generic/crashtests/499138-iframe.html | 17 + layout/generic/crashtests/499138.html | 18 + layout/generic/crashtests/499857-1.html | 33 + layout/generic/crashtests/499862-1.html | 9 + layout/generic/crashtests/501535-1.html | 6 + layout/generic/crashtests/503961-1.xhtml | 25 + layout/generic/crashtests/503961-2.html | 32 + layout/generic/crashtests/507566.html | 33 + layout/generic/crashtests/508154-1.xhtml | 1 + layout/generic/crashtests/508168-1.html | 6 + layout/generic/crashtests/508816-1.xhtml | 9 + layout/generic/crashtests/509749-1.html | 5 + layout/generic/crashtests/511482.html | 42 + layout/generic/crashtests/512724-1.html | 1 + layout/generic/crashtests/512725-1.html | 6 + layout/generic/crashtests/512749-1.html | 1 + layout/generic/crashtests/513110-1.html | 23 + layout/generic/crashtests/513110-2.xhtml | 5 + layout/generic/crashtests/513394-1.html | 16 + layout/generic/crashtests/514098-1.xhtml | 16 + layout/generic/crashtests/514800-1.html | 4 + layout/generic/crashtests/515811-1.html | 5 + layout/generic/crashtests/517968.html | 6 + layout/generic/crashtests/519031.xhtml | 6 + layout/generic/crashtests/520340.html | 2 + layout/generic/crashtests/522170-1.html | 1 + layout/generic/crashtests/526217.html | 16 + layout/generic/crashtests/533379-1.html | 16 + layout/generic/crashtests/533379-2.html | 16 + layout/generic/crashtests/534082-1.html | 7 + layout/generic/crashtests/534366-1.html | 38 + layout/generic/crashtests/534366-2.html | 42 + layout/generic/crashtests/536692-1.xhtml | 5 + layout/generic/crashtests/537645.xhtml | 11 + layout/generic/crashtests/541277-1.html | 5 + layout/generic/crashtests/541277-2.html | 5 + layout/generic/crashtests/541714-1.html | 3 + layout/generic/crashtests/541714-2.html | 3 + layout/generic/crashtests/542136-1.html | 23 + layout/generic/crashtests/545571-1.html | 8 + layout/generic/crashtests/547843-1.xhtml | 1 + layout/generic/crashtests/551635-1.html | 16 + layout/generic/crashtests/553504-1.xhtml | 4 + layout/generic/crashtests/564368-1.xhtml | 27 + layout/generic/crashtests/564968.xhtml | 30 + layout/generic/crashtests/569193-1.html | 6 + layout/generic/crashtests/570160.html | 53 + layout/generic/crashtests/570289-1.html | 1 + layout/generic/crashtests/571618-1.svg | 1 + layout/generic/crashtests/571975-1.html | 5 + layout/generic/crashtests/571995.xhtml | 8 + layout/generic/crashtests/574958.xhtml | 16 + layout/generic/crashtests/578977.html | 11 + layout/generic/crashtests/578977.xhtml | 10 + layout/generic/crashtests/580504-1.xhtml | 22 + layout/generic/crashtests/582793-1.html | 850 + layout/generic/crashtests/585598-1.xhtml | 7 + layout/generic/crashtests/586806-1.html | 27 + layout/generic/crashtests/586806-2.html | 1 + layout/generic/crashtests/586806-3.html | 9 + layout/generic/crashtests/586973-1.html | 9 + layout/generic/crashtests/589002-1.html | 4 + layout/generic/crashtests/590404.html | 1 + layout/generic/crashtests/591141.html | 7 + layout/generic/crashtests/592118.html | 4 + layout/generic/crashtests/594808-1.html | 7 + layout/generic/crashtests/595435-1.xhtml | 8 + layout/generic/crashtests/595740-1.html | 8 + layout/generic/crashtests/597240-1.xhtml | 20 + layout/generic/crashtests/600100.xhtml | 1 + layout/generic/crashtests/603490-1.html | 16 + layout/generic/crashtests/603510-1.html | 23 + layout/generic/crashtests/604314-1.html | 16 + layout/generic/crashtests/604843.html | 28 + layout/generic/crashtests/605340.html | 12 + layout/generic/crashtests/606642.xhtml | 16 + layout/generic/crashtests/613455-1.svg | 12 + layout/generic/crashtests/613629-1.xhtml | 14 + layout/generic/crashtests/616052-1.html | 4 + layout/generic/crashtests/619021.html | 5 + layout/generic/crashtests/621424-1.html | 1 + layout/generic/crashtests/621841-1.html | 20 + layout/generic/crashtests/622596.html | 6 + layout/generic/crashtests/641724.html | 315 + layout/generic/crashtests/645072-1.html | 16 + layout/generic/crashtests/645072-2.html | 17 + layout/generic/crashtests/646561-1.html | 2 + layout/generic/crashtests/646983-1.html | 6 + layout/generic/crashtests/647332-1.html | 2 + layout/generic/crashtests/650499-1.html | 15 + layout/generic/crashtests/654002-1.html | 24 + layout/generic/crashtests/654002-2.html | 26 + layout/generic/crashtests/655462-1.html | 10 + layout/generic/crashtests/656130-1.html | 17 + layout/generic/crashtests/656130-2.html | 24 + layout/generic/crashtests/660416.html | 17 + layout/generic/crashtests/665853.html | 29 + layout/generic/crashtests/667025.html | 22 + layout/generic/crashtests/673770.html | 20 + layout/generic/crashtests/679933-1.html | 13 + layout/generic/crashtests/681489-1.html | 1 + layout/generic/crashtests/682649-1.html | 18 + layout/generic/crashtests/683702-1.xhtml | 24 + layout/generic/crashtests/683712.html | 9 + layout/generic/crashtests/688996-1.html | 18 + layout/generic/crashtests/688996-2.html | 15 + layout/generic/crashtests/691210.html | 5 + layout/generic/crashtests/700031.xhtml | 9 + layout/generic/crashtests/709398-1.html | 12 + layout/generic/crashtests/718516.html | 70 + layout/generic/crashtests/723108.html | 10 + layout/generic/crashtests/724235.html | 28 + layout/generic/crashtests/724978.xhtml | 219 + layout/generic/crashtests/730559.html | 1 + layout/generic/crashtests/734777.html | 2 + layout/generic/crashtests/737313-1.html | 5 + layout/generic/crashtests/737313-2.html | 5 + layout/generic/crashtests/737313-3.html | 5 + layout/generic/crashtests/740199-1.xhtml | 1 + layout/generic/crashtests/742602.html | 6 + layout/generic/crashtests/743364.html | 24 + layout/generic/crashtests/747688.html | 6 + layout/generic/crashtests/750066-iframe.html | 32 + layout/generic/crashtests/750066.html | 34 + layout/generic/crashtests/757413-2.html | 12 + layout/generic/crashtests/757413.xhtml | 34 + layout/generic/crashtests/762764-1.html | 18 + layout/generic/crashtests/762902.html | 12 + layout/generic/crashtests/765409.html | 25 + layout/generic/crashtests/765621.html | 21 + layout/generic/crashtests/767765.html | 32 + layout/generic/crashtests/769120.html | 11 + layout/generic/crashtests/769303-1.html | 33 + layout/generic/crashtests/769303-2.html | 19 + layout/generic/crashtests/777838.html | 27 + layout/generic/crashtests/783228.html | 40 + layout/generic/crashtests/784600.html | 17 + layout/generic/crashtests/785555.html | 12 + layout/generic/crashtests/786740-1.html | 31 + layout/generic/crashtests/790252-1.html | 20 + layout/generic/crashtests/790252-2.html | 36 + layout/generic/crashtests/790260-1.html | 12 + layout/generic/crashtests/791601.xhtml | 4 + layout/generic/crashtests/794693.html | 9 + layout/generic/crashtests/798020-1.html | 4 + layout/generic/crashtests/798235-1.html | 8 + layout/generic/crashtests/799207-1.html | 6 + layout/generic/crashtests/799207-2.html | 6 + layout/generic/crashtests/801268-1.html | 6 + layout/generic/crashtests/804089-1.xhtml | 15 + layout/generic/crashtests/807565-1.html | 2 + layout/generic/crashtests/807565-2.html | 8 + layout/generic/crashtests/810303.html | 15 + layout/generic/crashtests/810726-2.html | 57 + layout/generic/crashtests/810726.html | 8 + layout/generic/crashtests/812822-1.html | 8 + layout/generic/crashtests/812879-1.html | 6 + layout/generic/crashtests/812879-2.html | 35 + layout/generic/crashtests/812893.html | 15 + layout/generic/crashtests/814995.html | 20 + layout/generic/crashtests/822910.xhtml | 34 + layout/generic/crashtests/824297-1.html | 12 + layout/generic/crashtests/825810-1.html | 11 + layout/generic/crashtests/825810-2.html | 11 + layout/generic/crashtests/826483-1.html | 16 + layout/generic/crashtests/826532-1.html | 15 + layout/generic/crashtests/827076.html | 2 + layout/generic/crashtests/827168-1.html | 12 + layout/generic/crashtests/836895.html | 13 + layout/generic/crashtests/837007.xhtml | 9 + layout/generic/crashtests/840787.html | 18 + layout/generic/crashtests/840818.html | 8 + layout/generic/crashtests/842132-1.html | 27 + layout/generic/crashtests/842166.html | 22 + layout/generic/crashtests/844529-1.html | 4 + layout/generic/crashtests/847130.xhtml | 15 + layout/generic/crashtests/847208.html | 16 + layout/generic/crashtests/847209.html | 16 + layout/generic/crashtests/847211-1.html | 19 + layout/generic/crashtests/849603.html | 47 + layout/generic/crashtests/849987.html | 9 + layout/generic/crashtests/850931.html | 32 + layout/generic/crashtests/851396-1.html | 9 + layout/generic/crashtests/854263-1.html | 27 + layout/generic/crashtests/862185.html | 5 + layout/generic/crashtests/863935.html | 25 + layout/generic/crashtests/866547-1.html | 14 + layout/generic/crashtests/866767-1.html | 7 + layout/generic/crashtests/868906.html | 54 + layout/generic/crashtests/876074-1.html | 20 + layout/generic/crashtests/876155.html | 15 + layout/generic/crashtests/883514-1.html | 18 + layout/generic/crashtests/883514-2.html | 15 + layout/generic/crashtests/885009-1.html | 7 + layout/generic/crashtests/893496-1.html | 12 + layout/generic/crashtests/893523.html | 7 + layout/generic/crashtests/898871-iframe.xhtml | 7 + layout/generic/crashtests/898871.html | 44 + layout/generic/crashtests/898871.jpg | Bin 0 -> 15000 bytes layout/generic/crashtests/914501.html | 17 + layout/generic/crashtests/914891.html | 9 + layout/generic/crashtests/915475.xhtml | 5 + layout/generic/crashtests/927558.html | 24 + layout/generic/crashtests/942794-1.html | 20 + layout/generic/crashtests/943509-1.html | 9 + layout/generic/crashtests/944909-1.html | 9 + layout/generic/crashtests/946167-1.html | 20 + layout/generic/crashtests/947158-iframe.html | 777 + layout/generic/crashtests/947158.html | 32 + layout/generic/crashtests/949932.html | 13 + layout/generic/crashtests/961859.html | 18 + layout/generic/crashtests/963878.html | 37 + layout/generic/crashtests/964078.html | 4 + layout/generic/crashtests/970710.html | 40 + layout/generic/crashtests/973701-1.xhtml | 5 + layout/generic/crashtests/973701-2.xhtml | 6 + layout/generic/crashtests/986899.html | 12 + layout/generic/crashtests/crashtests.list | 821 + .../crashtests/details-containing-only-text.html | 9 + .../crashtests/details-display-none-summary-1.html | 11 + .../crashtests/details-display-none-summary-2.html | 12 + .../crashtests/details-display-none-summary-3.html | 13 + .../crashtests/details-open-overflow-auto.html | 39 + .../crashtests/details-open-overflow-hidden.html | 39 + .../generic/crashtests/details-three-columns.html | 30 + layout/generic/crashtests/empty.html | 1 + layout/generic/crashtests/file_324318-1.html | 1 + .../generic/crashtests/first-letter-638937-1.html | 45 + .../generic/crashtests/first-letter-638937-2.html | 11 + .../generic/crashtests/flex-nested-abspos-1.html | 7 + .../generic/crashtests/font-inflation-762332.html | 2 + layout/generic/crashtests/image.jpg | Bin 0 -> 2646 bytes .../crashtests/large-border-radius-dashed.html | 1 + .../crashtests/large-border-radius-dashed2.html | 1 + .../crashtests/large-border-radius-dotted.html | 1 + .../crashtests/large-border-radius-dotted2.html | 1 + .../generic/crashtests/outline-on-frameset.xhtml | 1 + layout/generic/crashtests/simple_blank.swf | Bin 0 -> 37 bytes layout/generic/crashtests/solidblue.png | Bin 0 -> 135 bytes .../crashtests/summary-position-out-of-flow.html | 30 + .../crashtests/text-overflow-bug666751-1.html | 12 + .../crashtests/text-overflow-bug666751-2.html | 12 + .../crashtests/text-overflow-bug670564.xhtml | 3 + .../crashtests/text-overflow-bug671796.xhtml | 5 + .../crashtests/text-overflow-bug713610.html | 6 + .../crashtests/text-overflow-form-elements.html | 144 + .../generic/crashtests/text-overflow-iframe.html | 115 + layout/generic/folder.png | Bin 0 -> 529 bytes layout/generic/frame-graph.py | 41 + layout/generic/jar.mn | 6 + layout/generic/moz.build | 288 + layout/generic/nsAbsoluteContainingBlock.cpp | 888 + layout/generic/nsAbsoluteContainingBlock.h | 182 + layout/generic/nsAtomicContainerFrame.h | 44 + layout/generic/nsBackdropFrame.cpp | 79 + layout/generic/nsBackdropFrame.h | 40 + layout/generic/nsBlockDebugFlags.h | 26 + layout/generic/nsBlockFrame.cpp | 8301 ++ layout/generic/nsBlockFrame.h | 1048 + layout/generic/nsBlockReflowContext.cpp | 440 + layout/generic/nsBlockReflowContext.h | 91 + layout/generic/nsCanvasFrame.cpp | 861 + layout/generic/nsCanvasFrame.h | 195 + layout/generic/nsColumnSetFrame.cpp | 1355 + layout/generic/nsColumnSetFrame.h | 201 + layout/generic/nsContainerFrame.cpp | 3079 + layout/generic/nsContainerFrame.h | 1211 + layout/generic/nsContainerFrameInlines.h | 100 + layout/generic/nsDirection.h | 19 + layout/generic/nsFirstLetterFrame.cpp | 448 + layout/generic/nsFirstLetterFrame.h | 93 + layout/generic/nsFlexContainerFrame.cpp | 6019 + layout/generic/nsFlexContainerFrame.h | 655 + layout/generic/nsFloatManager.cpp | 3007 + layout/generic/nsFloatManager.h | 464 + layout/generic/nsFontInflationData.cpp | 373 + layout/generic/nsFontInflationData.h | 70 + layout/generic/nsFrameList.cpp | 478 + layout/generic/nsFrameList.h | 496 + layout/generic/nsFrameSelection.cpp | 3378 + layout/generic/nsFrameSelection.h | 1196 + layout/generic/nsFrameSetFrame.cpp | 1534 + layout/generic/nsFrameSetFrame.h | 198 + layout/generic/nsFrameState.cpp | 109 + layout/generic/nsFrameState.h | 74 + layout/generic/nsFrameStateBits.h | 721 + layout/generic/nsGfxScrollFrame.cpp | 7955 ++ layout/generic/nsGfxScrollFrame.h | 1092 + layout/generic/nsGridContainerFrame.cpp | 9947 ++ layout/generic/nsGridContainerFrame.h | 652 + layout/generic/nsHTMLCanvasFrame.cpp | 534 + layout/generic/nsHTMLCanvasFrame.h | 98 + layout/generic/nsHTMLParts.h | 194 + layout/generic/nsIAnonymousContentCreator.h | 70 + layout/generic/nsIFrame.cpp | 12506 ++ layout/generic/nsIFrame.h | 5606 + layout/generic/nsIFrameInlines.h | 189 + layout/generic/nsILineIterator.cpp | 75 + layout/generic/nsILineIterator.h | 133 + layout/generic/nsIScrollPositionListener.h | 22 + layout/generic/nsIScrollableFrame.h | 647 + layout/generic/nsIStatefulFrame.h | 40 + layout/generic/nsImageFrame.cpp | 2902 + layout/generic/nsImageFrame.h | 470 + layout/generic/nsImageMap.cpp | 879 + layout/generic/nsImageMap.h | 112 + layout/generic/nsInlineFrame.cpp | 1083 + layout/generic/nsInlineFrame.h | 220 + layout/generic/nsIntervalSet.cpp | 76 + layout/generic/nsIntervalSet.h | 72 + layout/generic/nsLeafFrame.cpp | 63 + layout/generic/nsLeafFrame.h | 91 + layout/generic/nsLineBox.cpp | 626 + layout/generic/nsLineBox.h | 1570 + layout/generic/nsLineLayout.cpp | 3453 + layout/generic/nsLineLayout.h | 693 + layout/generic/nsPageContentFrame.cpp | 452 + layout/generic/nsPageContentFrame.h | 84 + layout/generic/nsPageFrame.cpp | 958 + layout/generic/nsPageFrame.h | 172 + layout/generic/nsPageSequenceFrame.cpp | 781 + layout/generic/nsPageSequenceFrame.h | 191 + layout/generic/nsPlaceholderFrame.cpp | 228 + layout/generic/nsPlaceholderFrame.h | 187 + layout/generic/nsQueryFrame.h | 145 + layout/generic/nsRubyBaseContainerFrame.cpp | 830 + layout/generic/nsRubyBaseContainerFrame.h | 94 + layout/generic/nsRubyBaseFrame.cpp | 44 + layout/generic/nsRubyBaseFrame.h | 41 + layout/generic/nsRubyContentFrame.cpp | 39 + layout/generic/nsRubyContentFrame.h | 34 + layout/generic/nsRubyFrame.cpp | 427 + layout/generic/nsRubyFrame.h | 67 + layout/generic/nsRubyTextContainerFrame.cpp | 173 + layout/generic/nsRubyTextContainerFrame.h | 72 + layout/generic/nsRubyTextFrame.cpp | 79 + layout/generic/nsRubyTextFrame.h | 55 + layout/generic/nsSplittableFrame.cpp | 277 + layout/generic/nsSplittableFrame.h | 133 + layout/generic/nsSubDocumentFrame.cpp | 1435 + layout/generic/nsSubDocumentFrame.h | 244 + layout/generic/nsTextFrame.cpp | 10215 ++ layout/generic/nsTextFrame.h | 1040 + layout/generic/nsTextFrameUtils.cpp | 408 + layout/generic/nsTextFrameUtils.h | 196 + layout/generic/nsTextPaintStyle.cpp | 575 + layout/generic/nsTextPaintStyle.h | 161 + layout/generic/nsTextRunTransformations.cpp | 896 + layout/generic/nsTextRunTransformations.h | 248 + layout/generic/nsVideoFrame.cpp | 746 + layout/generic/nsVideoFrame.h | 136 + layout/generic/test/bug1174521.html | 14 + layout/generic/test/bug344830_testembed.svg | 8 + layout/generic/test/bug421839-2-page.html | 55 + layout/generic/test/bug633762_iframe.html | 8 + layout/generic/test/chrome.ini | 20 + layout/generic/test/file_BrokenImageReference.png | Bin 0 -> 253 bytes layout/generic/test/file_Dolske.png | Bin 0 -> 5976 bytes layout/generic/test/file_IconTestServer.sjs | 93 + layout/generic/test/file_LoadingImageReference.png | Bin 0 -> 268 bytes layout/generic/test/file_SlowImage.sjs | 45 + layout/generic/test/file_SlowPage.sjs | 43 + layout/generic/test/file_SlowTallImage.sjs | 21 + layout/generic/test/file_bug1307853.html | 23 + layout/generic/test/file_bug1566783.html | 24 + layout/generic/test/file_bug448987.html | 50 + layout/generic/test/file_bug448987_notref.html | 20 + layout/generic/test/file_bug448987_ref.html | 50 + layout/generic/test/file_bug449653_1.html | 18 + layout/generic/test/file_bug449653_1_ref.html | 13 + layout/generic/test/file_bug514732_window.xhtml | 82 + layout/generic/test/file_bug579767_1.html | 10 + layout/generic/test/file_bug579767_2.html | 10 + .../test/file_reframe_for_lazy_load_image.html | 38 + .../generic/test/file_scroll_position_restore.html | 111186 ++++++++++++++++++ .../file_scroll_position_restore_no_bfcache.html | 30 + ..._taintedfilters_feDisplacementMap-tainted-1.svg | 13 + ..._taintedfilters_feDisplacementMap-tainted-2.svg | 13 + ..._taintedfilters_feDisplacementMap-tainted-3.svg | 17 + ...aintedfilters_feDisplacementMap-tainted-ref.svg | 5 + ...aintedfilters_feDisplacementMap-untainted-1.svg | 13 + ...aintedfilters_feDisplacementMap-untainted-2.svg | 13 + ...ntedfilters_feDisplacementMap-untainted-ref.svg | 5 + ...e_taintedfilters_red-flood-for-feImage-cors.svg | 5 + ...filters_red-flood-for-feImage-cors.svg^headers^ | 4 + .../file_taintedfilters_red-flood-for-feImage.svg | 5 + .../test/frame_selection_underline-ref.xhtml | 46 + layout/generic/test/frame_selection_underline.css | 51 + .../generic/test/frame_selection_underline.xhtml | 48 + .../generic/test/frame_visibility_in_iframe.html | 52 + .../test/frame_visibility_in_iframe_child.html | 2 + layout/generic/test/mochitest.ini | 166 + .../test/page_scroll_with_fixed_pos_window.html | 128 + layout/generic/test/slow-stylesheet.sjs | 19 + layout/generic/test/test_backspace_delete.xhtml | 325 + layout/generic/test/test_bug1062406.html | 39 + layout/generic/test/test_bug1174521.html | 39 + layout/generic/test/test_bug1198135.html | 89 + layout/generic/test/test_bug1307853.html | 28 + layout/generic/test/test_bug1408607.html | 48 + layout/generic/test/test_bug1499961.html | 409 + layout/generic/test/test_bug1566783.html | 8 + layout/generic/test/test_bug1623764.html | 292 + layout/generic/test/test_bug1642588.html | 65 + layout/generic/test/test_bug1644511.html | 120 + layout/generic/test/test_bug1655135.html | 62 + layout/generic/test/test_bug1756831.html | 149 + layout/generic/test/test_bug1803209.html | 59 + layout/generic/test/test_bug240933.html | 69 + layout/generic/test/test_bug263683.html | 95 + layout/generic/test/test_bug288789.html | 117 + layout/generic/test/test_bug290397.html | 40 + layout/generic/test/test_bug323656.html | 51 + layout/generic/test/test_bug344830.html | 41 + layout/generic/test/test_bug348681.html | 490 + layout/generic/test/test_bug382429.html | 36 + layout/generic/test/test_bug384527.html | 34 + layout/generic/test/test_bug385751.html | 32 + layout/generic/test/test_bug389630.html | 32 + layout/generic/test/test_bug391747.html | 48 + layout/generic/test/test_bug392746.html | 71 + layout/generic/test/test_bug392923.html | 41 + layout/generic/test/test_bug394173.html | 33 + layout/generic/test/test_bug394239.html | 44 + layout/generic/test/test_bug402380.html | 36 + layout/generic/test/test_bug404872.html | 38 + layout/generic/test/test_bug405178.html | 51 + layout/generic/test/test_bug416168.html | 44 + layout/generic/test/test_bug421436.html | 31 + layout/generic/test/test_bug421839-1.html | 80 + layout/generic/test/test_bug421839-2.html | 35 + layout/generic/test/test_bug424627.html | 41 + layout/generic/test/test_bug438840.html | 52 + layout/generic/test/test_bug448860.html | 60 + layout/generic/test/test_bug448987.html | 72 + layout/generic/test/test_bug449653.html | 44 + layout/generic/test/test_bug460532.html | 58 + layout/generic/test/test_bug468167.html | 55 + layout/generic/test/test_bug469613.xhtml | 85 + layout/generic/test/test_bug469774.xhtml | 72 + layout/generic/test/test_bug470212.html | 58 + layout/generic/test/test_bug488417.html | 59 + layout/generic/test/test_bug496275.html | 288 + layout/generic/test/test_bug503813.html | 43 + layout/generic/test/test_bug507902.html | 382 + layout/generic/test/test_bug508115.xhtml | 66 + layout/generic/test/test_bug514732-2.xhtml | 40 + layout/generic/test/test_bug522632.html | 32 + layout/generic/test/test_bug524925.html | 35 + layout/generic/test/test_bug579767.html | 78 + layout/generic/test/test_bug589621.html | 37 + layout/generic/test/test_bug589623.html | 36 + layout/generic/test/test_bug597333.html | 38 + layout/generic/test/test_bug632379.xhtml | 223 + layout/generic/test/test_bug633762.html | 68 + layout/generic/test/test_bug666225.html | 42 + layout/generic/test/test_bug719503.html | 20 + layout/generic/test/test_bug719515.html | 22 + layout/generic/test/test_bug719518.html | 26 + layout/generic/test/test_bug719523.html | 20 + layout/generic/test/test_bug735641.html | 46 + layout/generic/test/test_bug748961.html | 45 + layout/generic/test/test_bug756984.html | 138 + layout/generic/test/test_bug784410.html | 86 + layout/generic/test/test_bug785324.html | 44 + layout/generic/test/test_bug791616.html | 65 + layout/generic/test/test_bug831780.html | 32 + layout/generic/test/test_bug841361.html | 56 + layout/generic/test/test_bug904810.html | 83 + layout/generic/test/test_bug938772.html | 23 + layout/generic/test/test_bug970363.html | 51 + layout/generic/test/test_crash_on_mouse_move.html | 36 + .../test/test_dynamic_reflow_root_disallowal.html | 747 + layout/generic/test/test_flex_interrupt.html | 107 + .../test/test_frame_visibility_in_iframe.html | 28 + .../test/test_grid_track_sizing_algo_001.html | 1641 + .../test/test_grid_track_sizing_algo_002.html | 1641 + layout/generic/test/test_image_selection.html | 90 + layout/generic/test/test_image_selection_2.html | 42 + layout/generic/test/test_image_selection_3.html | 48 + .../test_image_selection_in_contenteditable.html | 85 + .../test/test_intrinsic_size_on_loading.html | 53 + .../test/test_key_enter_open_second_summary.html | 28 + .../test/test_key_enter_prevent_default.html | 31 + .../test/test_key_enter_single_summary.html | 27 + .../test/test_key_space_single_summary.html | 26 + .../generic/test/test_movement_by_characters.html | 97 + layout/generic/test/test_movement_by_words.html | 624 + layout/generic/test/test_overflow_event.html | 50 + .../test/test_overlay_scrollbar_position.html | 42 + .../test/test_page_scroll_with_fixed_pos.html | 17 + .../test/test_reframe_for_lazy_load_image.html | 28 + .../test/test_scroll_animation_restore.html | 128 + layout/generic/test/test_scroll_behavior.html | 262 + .../test/test_scroll_on_display_contents.html | 187 + .../generic/test/test_scroll_position_iframe.html | 37 + .../generic/test/test_scroll_position_restore.html | 45 + .../test_scroll_position_restore_after_stop.html | 76 + .../test_scroll_position_restore_no_bfcache.html | 37 + .../test/test_scrollframe_abspos_interrupt.html | 60 + ...selection_changes_with_middle_mouse_button.html | 335 + .../generic/test/test_selection_doubleclick.html | 93 + layout/generic/test/test_selection_expanding.html | 419 + .../test/test_selection_multiclick_drag.html | 137 + .../test/test_selection_preventDefault.html | 173 + .../test/test_selection_splitText-normalize.html | 172 + .../generic/test/test_selection_touchevents.html | 55 + .../generic/test/test_selection_tripleclick.html | 63 + layout/generic/test/test_selection_underline.html | 246 + layout/generic/test/test_taintedfilters.html | 96 + 1170 files changed, 276627 insertions(+) create mode 100644 layout/generic/AnonymousContentKey.h create mode 100644 layout/generic/AspectRatio.cpp create mode 100644 layout/generic/AspectRatio.h create mode 100644 layout/generic/AutoCopyListener.h create mode 100644 layout/generic/BRFrame.cpp create mode 100644 layout/generic/BlockReflowState.cpp create mode 100644 layout/generic/BlockReflowState.h create mode 100644 layout/generic/CSSAlignUtils.cpp create mode 100644 layout/generic/CSSAlignUtils.h create mode 100644 layout/generic/CSSOrderAwareFrameIterator.cpp create mode 100644 layout/generic/CSSOrderAwareFrameIterator.h create mode 100644 layout/generic/ColumnSetWrapperFrame.cpp create mode 100644 layout/generic/ColumnSetWrapperFrame.h create mode 100644 layout/generic/ColumnUtils.cpp create mode 100644 layout/generic/ColumnUtils.h create mode 100644 layout/generic/FrameClass.py create mode 100644 layout/generic/FrameClasses.py create mode 100644 layout/generic/GenerateFrameLists.py create mode 100644 layout/generic/JustificationUtils.h create mode 100644 layout/generic/LayoutMessageUtils.h create mode 100644 layout/generic/MathMLTextRunFactory.cpp create mode 100644 layout/generic/MathMLTextRunFactory.h create mode 100644 layout/generic/MiddleCroppingBlockFrame.cpp create mode 100644 layout/generic/MiddleCroppingBlockFrame.h create mode 100644 layout/generic/PrintedSheetFrame.cpp create mode 100644 layout/generic/PrintedSheetFrame.h create mode 100644 layout/generic/ReflowInput.cpp create mode 100644 layout/generic/ReflowInput.h create mode 100644 layout/generic/ReflowOutput.cpp create mode 100644 layout/generic/ReflowOutput.h create mode 100644 layout/generic/RubyUtils.cpp create mode 100644 layout/generic/RubyUtils.h create mode 100644 layout/generic/ScrollAnchorContainer.cpp create mode 100644 layout/generic/ScrollAnchorContainer.h create mode 100644 layout/generic/ScrollAnimationBezierPhysics.cpp create mode 100644 layout/generic/ScrollAnimationBezierPhysics.h create mode 100644 layout/generic/ScrollAnimationMSDPhysics.cpp create mode 100644 layout/generic/ScrollAnimationMSDPhysics.h create mode 100644 layout/generic/ScrollAnimationPhysics.h create mode 100644 layout/generic/ScrollGeneration.cpp create mode 100644 layout/generic/ScrollGeneration.h create mode 100644 layout/generic/ScrollOrigin.h create mode 100644 layout/generic/ScrollPositionUpdate.cpp create mode 100644 layout/generic/ScrollPositionUpdate.h create mode 100644 layout/generic/ScrollSnap.cpp create mode 100644 layout/generic/ScrollSnap.h create mode 100644 layout/generic/ScrollSnapTargetId.h create mode 100644 layout/generic/ScrollVelocityQueue.cpp create mode 100644 layout/generic/ScrollVelocityQueue.h create mode 100644 layout/generic/ScrollbarActivity.cpp create mode 100644 layout/generic/ScrollbarActivity.h create mode 100644 layout/generic/ScrollbarPreferences.h create mode 100644 layout/generic/StickyScrollContainer.cpp create mode 100644 layout/generic/StickyScrollContainer.h create mode 100644 layout/generic/TextDrawTarget.h create mode 100644 layout/generic/TextOverflow.cpp create mode 100644 layout/generic/TextOverflow.h create mode 100644 layout/generic/ViewportFrame.cpp create mode 100644 layout/generic/ViewportFrame.h create mode 100644 layout/generic/Visibility.h create mode 100644 layout/generic/WBRFrame.cpp create mode 100644 layout/generic/WritingModes.h create mode 100644 layout/generic/broken-image.png create mode 100644 layout/generic/crashtests/1001233.html create mode 100644 layout/generic/crashtests/1001258-1.html create mode 100644 layout/generic/crashtests/1001994.html create mode 100644 layout/generic/crashtests/1003441.xhtml create mode 100644 layout/generic/crashtests/1015562.html create mode 100644 layout/generic/crashtests/1015563-1.html create mode 100644 layout/generic/crashtests/1015563-2.html create mode 100644 layout/generic/crashtests/1015844.html create mode 100644 layout/generic/crashtests/1032450.html create mode 100644 layout/generic/crashtests/1032613-1.svg create mode 100644 layout/generic/crashtests/1032613-2.html create mode 100644 layout/generic/crashtests/1037903.html create mode 100644 layout/generic/crashtests/1039454-1.html create mode 100644 layout/generic/crashtests/1042489.html create mode 100644 layout/generic/crashtests/1054010-1.html create mode 100644 layout/generic/crashtests/1058954-1.html create mode 100644 layout/generic/crashtests/1059138-1.html create mode 100644 layout/generic/crashtests/1102175-2.html create mode 100644 layout/generic/crashtests/1134531.html create mode 100644 layout/generic/crashtests/1134667.html create mode 100644 layout/generic/crashtests/1137723-1.html create mode 100644 layout/generic/crashtests/1137723-2.html create mode 100644 layout/generic/crashtests/1140043-1.html create mode 100644 layout/generic/crashtests/1140043-2.html create mode 100644 layout/generic/crashtests/1140043-3.html create mode 100644 layout/generic/crashtests/1140268-1.html create mode 100644 layout/generic/crashtests/1145768.html create mode 100644 layout/generic/crashtests/1145931.html create mode 100644 layout/generic/crashtests/1146103.html create mode 100644 layout/generic/crashtests/1146107.html create mode 100644 layout/generic/crashtests/1146114.html create mode 100644 layout/generic/crashtests/1153478-iframe.html create mode 100644 layout/generic/crashtests/1153478.html create mode 100644 layout/generic/crashtests/1153695.html create mode 100644 layout/generic/crashtests/1156222.html create mode 100644 layout/generic/crashtests/1156257.html create mode 100644 layout/generic/crashtests/1157011.html create mode 100644 layout/generic/crashtests/1169420-1.html create mode 100644 layout/generic/crashtests/1169420-2.html create mode 100644 layout/generic/crashtests/1178783-1.html create mode 100644 layout/generic/crashtests/1183431.html create mode 100644 layout/generic/crashtests/1186147-1.html create mode 100644 layout/generic/crashtests/1209952.html create mode 100644 layout/generic/crashtests/1221112-1.html create mode 100644 layout/generic/crashtests/1221112-2.html create mode 100644 layout/generic/crashtests/1221874-1.html create mode 100644 layout/generic/crashtests/1221904.html create mode 100644 layout/generic/crashtests/1222783.xhtml create mode 100644 layout/generic/crashtests/1223522.xhtml create mode 100644 layout/generic/crashtests/1223568-1.html create mode 100644 layout/generic/crashtests/1223568-2.html create mode 100644 layout/generic/crashtests/1224230-1.html create mode 100644 layout/generic/crashtests/1225005.html create mode 100644 layout/generic/crashtests/1225118.html create mode 100644 layout/generic/crashtests/1225376.html create mode 100644 layout/generic/crashtests/1225592.html create mode 100644 layout/generic/crashtests/1229437-1.html create mode 100644 layout/generic/crashtests/1229437-2.html create mode 100644 layout/generic/crashtests/1230378.xhtml create mode 100644 layout/generic/crashtests/1233191.html create mode 100644 layout/generic/crashtests/1233607.html create mode 100644 layout/generic/crashtests/1234701-1.html create mode 100644 layout/generic/crashtests/1234701-2.html create mode 100644 layout/generic/crashtests/1248227.html create mode 100644 layout/generic/crashtests/1271765.html create mode 100644 layout/generic/crashtests/1272983-1.html create mode 100644 layout/generic/crashtests/1272983-2.html create mode 100644 layout/generic/crashtests/1275059.html create mode 100644 layout/generic/crashtests/1278007.html create mode 100644 layout/generic/crashtests/1278080.html create mode 100644 layout/generic/crashtests/1278461-1.html create mode 100644 layout/generic/crashtests/1278461-2.html create mode 100644 layout/generic/crashtests/1279814.html create mode 100644 layout/generic/crashtests/1281102.html create mode 100644 layout/generic/crashtests/1297427-non-equal-centers.html create mode 100644 layout/generic/crashtests/1304441.html create mode 100644 layout/generic/crashtests/1308876-1.html create mode 100644 layout/generic/crashtests/1316649.html create mode 100644 layout/generic/crashtests/1316884-1.html create mode 100644 layout/generic/crashtests/1343552-1.html create mode 100644 layout/generic/crashtests/1343552-2.html create mode 100644 layout/generic/crashtests/1346454-1.html create mode 100644 layout/generic/crashtests/1346454-2.html create mode 100644 layout/generic/crashtests/1349650.html create mode 100644 layout/generic/crashtests/1349816-1.html create mode 100644 layout/generic/crashtests/1350372.html create mode 100644 layout/generic/crashtests/1364361-1.html create mode 100644 layout/generic/crashtests/1367413-1.html create mode 100644 layout/generic/crashtests/1368617-1.html create mode 100644 layout/generic/crashtests/1373586.html create mode 100644 layout/generic/crashtests/1375858.html create mode 100644 layout/generic/crashtests/1381134-2.html create mode 100644 layout/generic/crashtests/1381134.html create mode 100644 layout/generic/crashtests/1401420-1.html create mode 100644 layout/generic/crashtests/1401709.html create mode 100644 layout/generic/crashtests/1401807.html create mode 100644 layout/generic/crashtests/1404222-empty-shape.html create mode 100644 layout/generic/crashtests/1405443.html create mode 100644 layout/generic/crashtests/1405813.html create mode 100644 layout/generic/crashtests/1405896.html create mode 100644 layout/generic/crashtests/1406252-1.html create mode 100644 layout/generic/crashtests/1415185.html create mode 100644 layout/generic/crashtests/1416544.html create mode 100644 layout/generic/crashtests/1427824.html create mode 100644 layout/generic/crashtests/1431781-2.html create mode 100644 layout/generic/crashtests/1431781.html create mode 100644 layout/generic/crashtests/1458028.html create mode 100644 layout/generic/crashtests/1459697.html create mode 100644 layout/generic/crashtests/1460158-1.html create mode 100644 layout/generic/crashtests/1460158-2.html create mode 100644 layout/generic/crashtests/1460158-3.html create mode 100644 layout/generic/crashtests/1461039.html create mode 100644 layout/generic/crashtests/1461979-1.html create mode 100644 layout/generic/crashtests/1463977.html create mode 100644 layout/generic/crashtests/1466224.html create mode 100644 layout/generic/crashtests/1467239.html create mode 100644 layout/generic/crashtests/1472403.html create mode 100644 layout/generic/crashtests/1474768.html create mode 100644 layout/generic/crashtests/1478178.html create mode 100644 layout/generic/crashtests/1483972.html create mode 100644 layout/generic/crashtests/1486457.html create mode 100644 layout/generic/crashtests/1488762-1.html create mode 100644 layout/generic/crashtests/1488910-1.html create mode 100644 layout/generic/crashtests/1488910-2.html create mode 100644 layout/generic/crashtests/1489287.html create mode 100644 layout/generic/crashtests/1489770.html create mode 100644 layout/generic/crashtests/1489863.html create mode 100644 layout/generic/crashtests/1490032.html create mode 100644 layout/generic/crashtests/1490685.html create mode 100644 layout/generic/crashtests/1493708.html create mode 100644 layout/generic/crashtests/1493710.html create mode 100644 layout/generic/crashtests/1493741.html create mode 100644 layout/generic/crashtests/1494380.html create mode 100644 layout/generic/crashtests/1505817.html create mode 100644 layout/generic/crashtests/1506216.html create mode 100644 layout/generic/crashtests/1506306.html create mode 100644 layout/generic/crashtests/1507196.html create mode 100644 layout/generic/crashtests/1513275.html create mode 100644 layout/generic/crashtests/1513282.html create mode 100644 layout/generic/crashtests/1515124.html create mode 100644 layout/generic/crashtests/1517033.html create mode 100644 layout/generic/crashtests/1517297.html create mode 100644 layout/generic/crashtests/1520798-1.xhtml create mode 100644 layout/generic/crashtests/1520798-2.html create mode 100644 layout/generic/crashtests/1528771.html create mode 100644 layout/generic/crashtests/1539656.html create mode 100644 layout/generic/crashtests/1542441.html create mode 100644 layout/generic/crashtests/1543140-1.html create mode 100644 layout/generic/crashtests/1544060-1.html create mode 100644 layout/generic/crashtests/1544060-2.html create mode 100644 layout/generic/crashtests/1553824.html create mode 100644 layout/generic/crashtests/1554824.html create mode 100644 layout/generic/crashtests/1555142.html create mode 100644 layout/generic/crashtests/1560349.html create mode 100644 layout/generic/crashtests/1560397-2.html create mode 100644 layout/generic/crashtests/1560397.html create mode 100644 layout/generic/crashtests/1562105.html create mode 100644 layout/generic/crashtests/1563131.html create mode 100644 layout/generic/crashtests/1568001-1.html create mode 100644 layout/generic/crashtests/1568001-2.html create mode 100644 layout/generic/crashtests/1569639.html create mode 100644 layout/generic/crashtests/1571239.html create mode 100644 layout/generic/crashtests/1571460.html create mode 100644 layout/generic/crashtests/1571598.html create mode 100644 layout/generic/crashtests/1571897.html create mode 100644 layout/generic/crashtests/1572901.html create mode 100644 layout/generic/crashtests/1573216.html create mode 100644 layout/generic/crashtests/1574552.html create mode 100644 layout/generic/crashtests/1574993.html create mode 100644 layout/generic/crashtests/1582019.html create mode 100644 layout/generic/crashtests/1586470.html create mode 100644 layout/generic/crashtests/1588955-very-large-frameset.html create mode 100644 layout/generic/crashtests/1590569.html create mode 100644 layout/generic/crashtests/1596310.html create mode 100644 layout/generic/crashtests/1601819-1.html create mode 100644 layout/generic/crashtests/1608851-1.html create mode 100644 layout/generic/crashtests/1608851-2.html create mode 100644 layout/generic/crashtests/1613210.html create mode 100644 layout/generic/crashtests/1614101.html create mode 100644 layout/generic/crashtests/1618312.html create mode 100644 layout/generic/crashtests/1618564.html create mode 100644 layout/generic/crashtests/1625051-1.html create mode 100644 layout/generic/crashtests/1625051-2.html create mode 100644 layout/generic/crashtests/1626970.html create mode 100644 layout/generic/crashtests/1628804.html create mode 100644 layout/generic/crashtests/1629575-1.html create mode 100644 layout/generic/crashtests/1629575-2.html create mode 100644 layout/generic/crashtests/1630385.html create mode 100644 layout/generic/crashtests/1633434.html create mode 100644 layout/generic/crashtests/1633737-1.html create mode 100644 layout/generic/crashtests/1633737-2.html create mode 100644 layout/generic/crashtests/1633737-3.html create mode 100644 layout/generic/crashtests/1633737-4.html create mode 100644 layout/generic/crashtests/1633737-5.html create mode 100644 layout/generic/crashtests/1633828.html create mode 100644 layout/generic/crashtests/1638860-1.html create mode 100644 layout/generic/crashtests/1638860-2.html create mode 100644 layout/generic/crashtests/1638906.html create mode 100644 layout/generic/crashtests/1640028.html create mode 100644 layout/generic/crashtests/1640051.html create mode 100644 layout/generic/crashtests/1640275.html create mode 100644 layout/generic/crashtests/1644819.html create mode 100644 layout/generic/crashtests/1645549-1.html create mode 100644 layout/generic/crashtests/1648577.html create mode 100644 layout/generic/crashtests/1652618.html create mode 100644 layout/generic/crashtests/1652897.html create mode 100644 layout/generic/crashtests/1654925.html create mode 100644 layout/generic/crashtests/1663222.html create mode 100644 layout/generic/crashtests/1666592.html create mode 100644 layout/generic/crashtests/1670336.html create mode 100644 layout/generic/crashtests/1676970.html create mode 100644 layout/generic/crashtests/1677518-1.html create mode 100644 layout/generic/crashtests/1677518-1.jpg create mode 100644 layout/generic/crashtests/1677518-1.svg create mode 100644 layout/generic/crashtests/1679794.html create mode 100644 layout/generic/crashtests/1680406.html create mode 100644 layout/generic/crashtests/1681788.html create mode 100644 layout/generic/crashtests/1682032.html create mode 100644 layout/generic/crashtests/1682686-1.html create mode 100644 layout/generic/crashtests/1682686-2.html create mode 100644 layout/generic/crashtests/1682882.html create mode 100644 layout/generic/crashtests/1683126.html create mode 100644 layout/generic/crashtests/1697262-1.html create mode 100644 layout/generic/crashtests/1699263.html create mode 100644 layout/generic/crashtests/1699468.html create mode 100644 layout/generic/crashtests/1728319.html create mode 100644 layout/generic/crashtests/1730506.html create mode 100644 layout/generic/crashtests/1730570.html create mode 100644 layout/generic/crashtests/1734015.html create mode 100644 layout/generic/crashtests/1776079.html create mode 100644 layout/generic/crashtests/1791606.html create mode 100644 layout/generic/crashtests/1799749.html create mode 100644 layout/generic/crashtests/1807958.html create mode 100644 layout/generic/crashtests/1816574.html create mode 100644 layout/generic/crashtests/1821603.html create mode 100644 layout/generic/crashtests/1822118.html create mode 100644 layout/generic/crashtests/1825434.html create mode 100644 layout/generic/crashtests/225868-1-inner.html create mode 100644 layout/generic/crashtests/225868-1.html create mode 100644 layout/generic/crashtests/255468.xhtml create mode 100644 layout/generic/crashtests/255982-1.html create mode 100644 layout/generic/crashtests/255982-2.html create mode 100644 layout/generic/crashtests/255982-3.html create mode 100644 layout/generic/crashtests/255982-4.html create mode 100644 layout/generic/crashtests/25888-1.html create mode 100644 layout/generic/crashtests/25888-2.html create mode 100644 layout/generic/crashtests/264937-1.html create mode 100644 layout/generic/crashtests/265867-1.html create mode 100644 layout/generic/crashtests/265867-2.html create mode 100644 layout/generic/crashtests/286491.html create mode 100644 layout/generic/crashtests/289864-1.html create mode 100644 layout/generic/crashtests/289864-1.jpg create mode 100644 layout/generic/crashtests/295292-1.html create mode 100644 layout/generic/crashtests/295292-2.html create mode 100644 layout/generic/crashtests/302260-1.html create mode 100644 layout/generic/crashtests/307979-1.html create mode 100644 layout/generic/crashtests/309322-1.html create mode 100644 layout/generic/crashtests/309322-2.html create mode 100644 layout/generic/crashtests/309322-3.html create mode 100644 layout/generic/crashtests/309322-4.html create mode 100644 layout/generic/crashtests/310556-1.xhtml create mode 100644 layout/generic/crashtests/321224.xhtml create mode 100644 layout/generic/crashtests/322780-1.xhtml create mode 100644 layout/generic/crashtests/323381-1.html create mode 100644 layout/generic/crashtests/323381-2.html create mode 100644 layout/generic/crashtests/323386-1.html create mode 100644 layout/generic/crashtests/323389-1.html create mode 100644 layout/generic/crashtests/323389-2.html create mode 100644 layout/generic/crashtests/323493-1.html create mode 100644 layout/generic/crashtests/323495-1.html create mode 100644 layout/generic/crashtests/324318-1.html create mode 100644 layout/generic/crashtests/328946-1.html create mode 100644 layout/generic/crashtests/331284-1.xhtml create mode 100644 layout/generic/crashtests/331292.html create mode 100644 layout/generic/crashtests/334105-1.xhtml create mode 100644 layout/generic/crashtests/334107-1.xhtml create mode 100644 layout/generic/crashtests/334147-1.xhtml create mode 100644 layout/generic/crashtests/334148-1.xhtml create mode 100644 layout/generic/crashtests/334602-1.html create mode 100644 layout/generic/crashtests/337412-1.html create mode 100644 layout/generic/crashtests/337883-1.html create mode 100644 layout/generic/crashtests/337883-2.html create mode 100644 layout/generic/crashtests/339769-1.html create mode 100644 layout/generic/crashtests/342322-1.html create mode 100644 layout/generic/crashtests/343206-1.xhtml create mode 100644 layout/generic/crashtests/344557-1.html create mode 100644 layout/generic/crashtests/345139-1.xhtml create mode 100644 layout/generic/crashtests/345617-1.html create mode 100644 layout/generic/crashtests/348510-1.html create mode 100644 layout/generic/crashtests/348510-2.html create mode 100644 layout/generic/crashtests/348887-1-inner.html create mode 100644 layout/generic/crashtests/348887-1.html create mode 100644 layout/generic/crashtests/350370.html create mode 100644 layout/generic/crashtests/354458-1.html create mode 100644 layout/generic/crashtests/354458-2.html create mode 100644 layout/generic/crashtests/355426-1.html create mode 100644 layout/generic/crashtests/359371-1.html create mode 100644 layout/generic/crashtests/359371-2.html create mode 100644 layout/generic/crashtests/360599.html create mode 100644 layout/generic/crashtests/361109.html create mode 100644 layout/generic/crashtests/363448.html create mode 100644 layout/generic/crashtests/363722-1.html create mode 100644 layout/generic/crashtests/363722-2.html create mode 100644 layout/generic/crashtests/364220.html create mode 100644 layout/generic/crashtests/364407-1.html create mode 100644 layout/generic/crashtests/364686-1.xhtml create mode 100644 layout/generic/crashtests/366021-1.xhtml create mode 100644 layout/generic/crashtests/366667-1.html create mode 100644 layout/generic/crashtests/366952-1.html create mode 100644 layout/generic/crashtests/367246-1.html create mode 100644 layout/generic/crashtests/367360.html create mode 100644 layout/generic/crashtests/368330-1.html create mode 100644 layout/generic/crashtests/368461-1.xhtml create mode 100644 layout/generic/crashtests/368568.html create mode 100644 layout/generic/crashtests/368752.html create mode 100644 layout/generic/crashtests/368860-1.html create mode 100644 layout/generic/crashtests/368863-1.html create mode 100644 layout/generic/crashtests/369150-1.html create mode 100644 layout/generic/crashtests/369150-2.html create mode 100644 layout/generic/crashtests/369227-1.xhtml create mode 100644 layout/generic/crashtests/369542-1.html create mode 100644 layout/generic/crashtests/369542-2.html create mode 100644 layout/generic/crashtests/369547-1.html create mode 100644 layout/generic/crashtests/370174-1.html create mode 100644 layout/generic/crashtests/370174-2.html create mode 100644 layout/generic/crashtests/370174-3.html create mode 100644 layout/generic/crashtests/370699-1.html create mode 100644 layout/generic/crashtests/370794-1.html create mode 100644 layout/generic/crashtests/370884-1.xhtml create mode 100644 layout/generic/crashtests/371348-1.xhtml create mode 100644 layout/generic/crashtests/371561-1.html create mode 100644 layout/generic/crashtests/371566-1.xhtml create mode 100644 layout/generic/crashtests/372376-1.xhtml create mode 100644 layout/generic/crashtests/373859-1.html create mode 100644 layout/generic/crashtests/373868-1.xhtml create mode 100644 layout/generic/crashtests/375462-1.html create mode 100644 layout/generic/crashtests/375831.html create mode 100644 layout/generic/crashtests/376419.html create mode 100644 layout/generic/crashtests/377522.html create mode 100644 layout/generic/crashtests/37757-1.html create mode 100644 layout/generic/crashtests/379217-1.xhtml create mode 100644 layout/generic/crashtests/379217-2.xhtml create mode 100644 layout/generic/crashtests/379917-1.xhtml create mode 100644 layout/generic/crashtests/380012-1.html create mode 100644 layout/generic/crashtests/381152-1.html create mode 100644 layout/generic/crashtests/382129-1.xhtml create mode 100644 layout/generic/crashtests/382131-1.html create mode 100644 layout/generic/crashtests/382199-1.html create mode 100644 layout/generic/crashtests/382208-1.xhtml create mode 100644 layout/generic/crashtests/382262-1.html create mode 100644 layout/generic/crashtests/382396-1.xhtml create mode 100644 layout/generic/crashtests/383089-1.html create mode 100644 layout/generic/crashtests/385265-1.xhtml create mode 100644 layout/generic/crashtests/385295-1.xhtml create mode 100644 layout/generic/crashtests/385344-1.html create mode 100644 layout/generic/crashtests/385344-2.html create mode 100644 layout/generic/crashtests/385414-1.html create mode 100644 layout/generic/crashtests/385414-2.html create mode 100644 layout/generic/crashtests/385426-1.html create mode 100644 layout/generic/crashtests/385526.html create mode 100644 layout/generic/crashtests/385681.html create mode 100644 layout/generic/crashtests/386799-1.html create mode 100644 layout/generic/crashtests/386807-1.html create mode 100644 layout/generic/crashtests/386812-1.html create mode 100644 layout/generic/crashtests/386827-1.html create mode 100644 layout/generic/crashtests/387058-1.html create mode 100644 layout/generic/crashtests/387058-2.html create mode 100644 layout/generic/crashtests/387088-1.html create mode 100644 layout/generic/crashtests/387209-1.html create mode 100644 layout/generic/crashtests/387213-1.html create mode 100644 layout/generic/crashtests/387215-1.xhtml create mode 100644 layout/generic/crashtests/387219-1.xhtml create mode 100644 layout/generic/crashtests/387233-1.html create mode 100644 layout/generic/crashtests/387233-2.html create mode 100644 layout/generic/crashtests/387282-1.html create mode 100644 layout/generic/crashtests/388049.html create mode 100644 layout/generic/crashtests/388175-1.html create mode 100644 layout/generic/crashtests/388367-1.html create mode 100644 layout/generic/crashtests/388709-1.html create mode 100644 layout/generic/crashtests/389635-1.html create mode 100644 layout/generic/crashtests/390050-1.html create mode 100644 layout/generic/crashtests/390050-2.html create mode 100644 layout/generic/crashtests/390050-3.html create mode 100644 layout/generic/crashtests/390052.html create mode 100644 layout/generic/crashtests/390417.html create mode 100644 layout/generic/crashtests/390762-1.html create mode 100644 layout/generic/crashtests/391053-1.xhtml create mode 100644 layout/generic/crashtests/391894-1.html create mode 100644 layout/generic/crashtests/392698-1.html create mode 100644 layout/generic/crashtests/393758-1.xhtml create mode 100644 layout/generic/crashtests/393906-1.html create mode 100644 layout/generic/crashtests/393923-1.html create mode 100644 layout/generic/crashtests/393956-1.html create mode 100644 layout/generic/crashtests/393956-2.html create mode 100644 layout/generic/crashtests/393956-3.html create mode 100644 layout/generic/crashtests/393956-4.html create mode 100644 layout/generic/crashtests/394237-1.html create mode 100644 layout/generic/crashtests/394818-1.html create mode 100644 layout/generic/crashtests/394818-2.html create mode 100644 layout/generic/crashtests/394820-1.html create mode 100644 layout/generic/crashtests/395316-1.html create mode 100644 layout/generic/crashtests/395450-1.xhtml create mode 100644 layout/generic/crashtests/397007-1.html create mode 100644 layout/generic/crashtests/397187-1.html create mode 100644 layout/generic/crashtests/397844-1.xhtml create mode 100644 layout/generic/crashtests/397844-2.xhtml create mode 100644 layout/generic/crashtests/397852-1.xhtml create mode 100644 layout/generic/crashtests/398181-1.html create mode 100644 layout/generic/crashtests/398181-2.html create mode 100644 layout/generic/crashtests/398322-1.html create mode 100644 layout/generic/crashtests/398322-2.html create mode 100644 layout/generic/crashtests/398332-1.html create mode 100644 layout/generic/crashtests/398332-2.html create mode 100644 layout/generic/crashtests/398332-3.html create mode 100644 layout/generic/crashtests/399407-1.xhtml create mode 100644 layout/generic/crashtests/399412-1.html create mode 100644 layout/generic/crashtests/399843-1.html create mode 100644 layout/generic/crashtests/400078-1.html create mode 100644 layout/generic/crashtests/400190.html create mode 100644 layout/generic/crashtests/400223-1.html create mode 100644 layout/generic/crashtests/400232-1.html create mode 100644 layout/generic/crashtests/400244-1.html create mode 100644 layout/generic/crashtests/400768-1.xhtml create mode 100644 layout/generic/crashtests/400768-2.xhtml create mode 100644 layout/generic/crashtests/401042-2.html create mode 100644 layout/generic/crashtests/402380-1.html create mode 100644 layout/generic/crashtests/402380-2.html create mode 100644 layout/generic/crashtests/402872-1.html create mode 100644 layout/generic/crashtests/402872-2.html create mode 100644 layout/generic/crashtests/403004.html create mode 100644 layout/generic/crashtests/403143-1.html create mode 100644 layout/generic/crashtests/403576-1.html create mode 100644 layout/generic/crashtests/404140-1.html create mode 100644 layout/generic/crashtests/404146-1.html create mode 100644 layout/generic/crashtests/404204-1.html create mode 100644 layout/generic/crashtests/404215-1.html create mode 100644 layout/generic/crashtests/404215-2.html create mode 100644 layout/generic/crashtests/404215-3.html create mode 100644 layout/generic/crashtests/404219-1.html create mode 100644 layout/generic/crashtests/404219-2.html create mode 100644 layout/generic/crashtests/404624.html create mode 100644 layout/generic/crashtests/406137.html create mode 100644 layout/generic/crashtests/406380.html create mode 100644 layout/generic/crashtests/406902-1.html create mode 100644 layout/generic/crashtests/407009-1.xhtml create mode 100644 layout/generic/crashtests/408304-1.xhtml create mode 100644 layout/generic/crashtests/408602-1.html create mode 100644 layout/generic/crashtests/408737-1.html create mode 100644 layout/generic/crashtests/408737-2.html create mode 100644 layout/generic/crashtests/408749-1.xhtml create mode 100644 layout/generic/crashtests/408883-1.html create mode 100644 layout/generic/crashtests/410198.html create mode 100644 layout/generic/crashtests/410228-1.html create mode 100644 layout/generic/crashtests/410232-1.html create mode 100644 layout/generic/crashtests/410595-1.html create mode 100644 layout/generic/crashtests/411213-1.html create mode 100644 layout/generic/crashtests/411213-2.xml create mode 100644 layout/generic/crashtests/411835.html create mode 100644 layout/generic/crashtests/411851-1.html create mode 100644 layout/generic/crashtests/412014-1.html create mode 100644 layout/generic/crashtests/412201-1.xhtml create mode 100644 layout/generic/crashtests/412543-1.html create mode 100644 layout/generic/crashtests/413048-1.html create mode 100644 layout/generic/crashtests/413079-1.xhtml create mode 100644 layout/generic/crashtests/413079-2.xhtml create mode 100644 layout/generic/crashtests/413079-3.xhtml create mode 100644 layout/generic/crashtests/413085-1.html create mode 100644 layout/generic/crashtests/413085-2.html create mode 100644 layout/generic/crashtests/413582-1.xhtml create mode 100644 layout/generic/crashtests/413582-2.html create mode 100644 layout/generic/crashtests/413712-1.xhtml create mode 100644 layout/generic/crashtests/414061-1.html create mode 100644 layout/generic/crashtests/414180-1.xhtml create mode 100644 layout/generic/crashtests/414719-1.html create mode 100644 layout/generic/crashtests/415685-1.html create mode 100644 layout/generic/crashtests/415818.xhtml create mode 100644 layout/generic/crashtests/416165.html create mode 100644 layout/generic/crashtests/416264-1.html create mode 100644 layout/generic/crashtests/416476-1.html create mode 100644 layout/generic/crashtests/417848-1.xhtml create mode 100644 layout/generic/crashtests/417902-1.html create mode 100644 layout/generic/crashtests/417902-2.html create mode 100644 layout/generic/crashtests/418532-1.html create mode 100644 layout/generic/crashtests/418932-1.html create mode 100644 layout/generic/crashtests/419352.html create mode 100644 layout/generic/crashtests/420000-1.html create mode 100644 layout/generic/crashtests/420718.html create mode 100644 layout/generic/crashtests/421404-1.html create mode 100644 layout/generic/crashtests/421671.html create mode 100644 layout/generic/crashtests/422283-1.html create mode 100644 layout/generic/crashtests/422301-1.html create mode 100644 layout/generic/crashtests/423055-1.html create mode 100644 layout/generic/crashtests/423098.html create mode 100644 layout/generic/crashtests/423264-1.html create mode 100644 layout/generic/crashtests/424629.html create mode 100644 layout/generic/crashtests/425253-1.html create mode 100644 layout/generic/crashtests/426040-1.html create mode 100644 layout/generic/crashtests/426272-1.html create mode 100644 layout/generic/crashtests/428263-1.html create mode 100644 layout/generic/crashtests/429960-1.html create mode 100644 layout/generic/crashtests/429960-2.html create mode 100644 layout/generic/crashtests/429969-1.html create mode 100644 layout/generic/crashtests/429981-1.html create mode 100644 layout/generic/crashtests/430332-1.html create mode 100644 layout/generic/crashtests/430344-1.html create mode 100644 layout/generic/crashtests/430352-1.html create mode 100644 layout/generic/crashtests/430744-1.html create mode 100644 layout/generic/crashtests/430991.html create mode 100644 layout/generic/crashtests/431260-1.html create mode 100644 layout/generic/crashtests/431260-2.html create mode 100644 layout/generic/crashtests/435529.html create mode 100644 layout/generic/crashtests/436194-1.html create mode 100644 layout/generic/crashtests/436602-1.html create mode 100644 layout/generic/crashtests/436822-1.html create mode 100644 layout/generic/crashtests/436823.html create mode 100644 layout/generic/crashtests/436969-1.html create mode 100644 layout/generic/crashtests/437156-1.html create mode 100644 layout/generic/crashtests/437565-1.xhtml create mode 100644 layout/generic/crashtests/437565-2.xhtml create mode 100644 layout/generic/crashtests/437565-3.xhtml create mode 100644 layout/generic/crashtests/438259-1.html create mode 100644 layout/generic/crashtests/438266-1.html create mode 100644 layout/generic/crashtests/438509-1.html create mode 100644 layout/generic/crashtests/443528-1.html create mode 100644 layout/generic/crashtests/444230-1.html create mode 100644 layout/generic/crashtests/444484-1.html create mode 100644 layout/generic/crashtests/444726-1.xhtml create mode 100644 layout/generic/crashtests/444861-1.html create mode 100644 layout/generic/crashtests/445288.html create mode 100644 layout/generic/crashtests/448903-1.html create mode 100644 layout/generic/crashtests/448996-1.html create mode 100644 layout/generic/crashtests/451315-1.html create mode 100644 layout/generic/crashtests/451317-1.html create mode 100644 layout/generic/crashtests/451334-1.html create mode 100644 layout/generic/crashtests/452157-1.html create mode 100644 layout/generic/crashtests/452157-2.html create mode 100644 layout/generic/crashtests/452157-3.html create mode 100644 layout/generic/crashtests/453762-1.html create mode 100644 layout/generic/crashtests/455171-1.html create mode 100644 layout/generic/crashtests/455171-2.html create mode 100644 layout/generic/crashtests/455171-3.html create mode 100644 layout/generic/crashtests/455643-1.xhtml create mode 100644 layout/generic/crashtests/457375.html create mode 100644 layout/generic/crashtests/457380-1.html create mode 100644 layout/generic/crashtests/459968.html create mode 100644 layout/generic/crashtests/460910-1.xml create mode 100644 layout/generic/crashtests/461294-1.html create mode 100644 layout/generic/crashtests/462968.xhtml create mode 100644 layout/generic/crashtests/463350-1.html create mode 100644 layout/generic/crashtests/463350-2.html create mode 100644 layout/generic/crashtests/463350-3.html create mode 100644 layout/generic/crashtests/463741-1.html create mode 100644 layout/generic/crashtests/465651-1.html create mode 100644 layout/generic/crashtests/467137-1.html create mode 100644 layout/generic/crashtests/467213-1.html create mode 100644 layout/generic/crashtests/467487-1.html create mode 100644 layout/generic/crashtests/467493-1.html create mode 100644 layout/generic/crashtests/467493-2.html create mode 100644 layout/generic/crashtests/467875-1.xhtml create mode 100644 layout/generic/crashtests/467914-1.html create mode 100644 layout/generic/crashtests/468207-1.html create mode 100644 layout/generic/crashtests/468771-1.xhtml create mode 100644 layout/generic/crashtests/468771-2.xhtml create mode 100644 layout/generic/crashtests/469859-1.xhtml create mode 100644 layout/generic/crashtests/471360.html create mode 100644 layout/generic/crashtests/472587-1.xhtml create mode 100644 layout/generic/crashtests/472617-1.xhtml create mode 100644 layout/generic/crashtests/472774-1.html create mode 100644 layout/generic/crashtests/472776-1.html create mode 100644 layout/generic/crashtests/472950-1.html create mode 100644 layout/generic/crashtests/473278-1.xhtml create mode 100644 layout/generic/crashtests/473894-1.html create mode 100644 layout/generic/crashtests/476241-1.html create mode 100644 layout/generic/crashtests/477731-1.html create mode 100644 layout/generic/crashtests/477928.html create mode 100644 layout/generic/crashtests/478131-1.html create mode 100644 layout/generic/crashtests/478170-1.html create mode 100644 layout/generic/crashtests/478185-1.html create mode 100644 layout/generic/crashtests/478504.html create mode 100644 layout/generic/crashtests/479938-1.html create mode 100644 layout/generic/crashtests/480345-1.html create mode 100644 layout/generic/crashtests/481921-iframe.html create mode 100644 layout/generic/crashtests/481921.html create mode 100644 layout/generic/crashtests/481921.ogg create mode 100644 layout/generic/crashtests/489462-1.html create mode 100644 layout/generic/crashtests/489477.html create mode 100644 layout/generic/crashtests/489480-1.xhtml create mode 100644 layout/generic/crashtests/489647-1.html create mode 100644 layout/generic/crashtests/493111-1.html create mode 100644 layout/generic/crashtests/493118-1.html create mode 100644 layout/generic/crashtests/493649.html create mode 100644 layout/generic/crashtests/494283-1.xhtml create mode 100644 layout/generic/crashtests/494283-2.html create mode 100644 layout/generic/crashtests/494332-1.html create mode 100644 layout/generic/crashtests/495875-1.html create mode 100644 layout/generic/crashtests/495875-2.html create mode 100644 layout/generic/crashtests/496742.html create mode 100644 layout/generic/crashtests/499138-iframe.html create mode 100644 layout/generic/crashtests/499138.html create mode 100644 layout/generic/crashtests/499857-1.html create mode 100644 layout/generic/crashtests/499862-1.html create mode 100644 layout/generic/crashtests/501535-1.html create mode 100644 layout/generic/crashtests/503961-1.xhtml create mode 100644 layout/generic/crashtests/503961-2.html create mode 100644 layout/generic/crashtests/507566.html create mode 100644 layout/generic/crashtests/508154-1.xhtml create mode 100644 layout/generic/crashtests/508168-1.html create mode 100644 layout/generic/crashtests/508816-1.xhtml create mode 100644 layout/generic/crashtests/509749-1.html create mode 100644 layout/generic/crashtests/511482.html create mode 100644 layout/generic/crashtests/512724-1.html create mode 100644 layout/generic/crashtests/512725-1.html create mode 100644 layout/generic/crashtests/512749-1.html create mode 100644 layout/generic/crashtests/513110-1.html create mode 100644 layout/generic/crashtests/513110-2.xhtml create mode 100644 layout/generic/crashtests/513394-1.html create mode 100644 layout/generic/crashtests/514098-1.xhtml create mode 100644 layout/generic/crashtests/514800-1.html create mode 100644 layout/generic/crashtests/515811-1.html create mode 100644 layout/generic/crashtests/517968.html create mode 100644 layout/generic/crashtests/519031.xhtml create mode 100644 layout/generic/crashtests/520340.html create mode 100644 layout/generic/crashtests/522170-1.html create mode 100644 layout/generic/crashtests/526217.html create mode 100644 layout/generic/crashtests/533379-1.html create mode 100644 layout/generic/crashtests/533379-2.html create mode 100644 layout/generic/crashtests/534082-1.html create mode 100644 layout/generic/crashtests/534366-1.html create mode 100644 layout/generic/crashtests/534366-2.html create mode 100644 layout/generic/crashtests/536692-1.xhtml create mode 100644 layout/generic/crashtests/537645.xhtml create mode 100644 layout/generic/crashtests/541277-1.html create mode 100644 layout/generic/crashtests/541277-2.html create mode 100644 layout/generic/crashtests/541714-1.html create mode 100644 layout/generic/crashtests/541714-2.html create mode 100644 layout/generic/crashtests/542136-1.html create mode 100644 layout/generic/crashtests/545571-1.html create mode 100644 layout/generic/crashtests/547843-1.xhtml create mode 100644 layout/generic/crashtests/551635-1.html create mode 100644 layout/generic/crashtests/553504-1.xhtml create mode 100644 layout/generic/crashtests/564368-1.xhtml create mode 100644 layout/generic/crashtests/564968.xhtml create mode 100644 layout/generic/crashtests/569193-1.html create mode 100644 layout/generic/crashtests/570160.html create mode 100644 layout/generic/crashtests/570289-1.html create mode 100644 layout/generic/crashtests/571618-1.svg create mode 100644 layout/generic/crashtests/571975-1.html create mode 100644 layout/generic/crashtests/571995.xhtml create mode 100644 layout/generic/crashtests/574958.xhtml create mode 100644 layout/generic/crashtests/578977.html create mode 100644 layout/generic/crashtests/578977.xhtml create mode 100644 layout/generic/crashtests/580504-1.xhtml create mode 100644 layout/generic/crashtests/582793-1.html create mode 100644 layout/generic/crashtests/585598-1.xhtml create mode 100644 layout/generic/crashtests/586806-1.html create mode 100644 layout/generic/crashtests/586806-2.html create mode 100644 layout/generic/crashtests/586806-3.html create mode 100644 layout/generic/crashtests/586973-1.html create mode 100644 layout/generic/crashtests/589002-1.html create mode 100644 layout/generic/crashtests/590404.html create mode 100644 layout/generic/crashtests/591141.html create mode 100644 layout/generic/crashtests/592118.html create mode 100644 layout/generic/crashtests/594808-1.html create mode 100644 layout/generic/crashtests/595435-1.xhtml create mode 100644 layout/generic/crashtests/595740-1.html create mode 100644 layout/generic/crashtests/597240-1.xhtml create mode 100644 layout/generic/crashtests/600100.xhtml create mode 100644 layout/generic/crashtests/603490-1.html create mode 100644 layout/generic/crashtests/603510-1.html create mode 100644 layout/generic/crashtests/604314-1.html create mode 100644 layout/generic/crashtests/604843.html create mode 100644 layout/generic/crashtests/605340.html create mode 100644 layout/generic/crashtests/606642.xhtml create mode 100644 layout/generic/crashtests/613455-1.svg create mode 100644 layout/generic/crashtests/613629-1.xhtml create mode 100644 layout/generic/crashtests/616052-1.html create mode 100644 layout/generic/crashtests/619021.html create mode 100644 layout/generic/crashtests/621424-1.html create mode 100644 layout/generic/crashtests/621841-1.html create mode 100644 layout/generic/crashtests/622596.html create mode 100644 layout/generic/crashtests/641724.html create mode 100644 layout/generic/crashtests/645072-1.html create mode 100644 layout/generic/crashtests/645072-2.html create mode 100644 layout/generic/crashtests/646561-1.html create mode 100644 layout/generic/crashtests/646983-1.html create mode 100644 layout/generic/crashtests/647332-1.html create mode 100644 layout/generic/crashtests/650499-1.html create mode 100644 layout/generic/crashtests/654002-1.html create mode 100644 layout/generic/crashtests/654002-2.html create mode 100644 layout/generic/crashtests/655462-1.html create mode 100644 layout/generic/crashtests/656130-1.html create mode 100644 layout/generic/crashtests/656130-2.html create mode 100644 layout/generic/crashtests/660416.html create mode 100644 layout/generic/crashtests/665853.html create mode 100644 layout/generic/crashtests/667025.html create mode 100644 layout/generic/crashtests/673770.html create mode 100644 layout/generic/crashtests/679933-1.html create mode 100644 layout/generic/crashtests/681489-1.html create mode 100644 layout/generic/crashtests/682649-1.html create mode 100644 layout/generic/crashtests/683702-1.xhtml create mode 100644 layout/generic/crashtests/683712.html create mode 100644 layout/generic/crashtests/688996-1.html create mode 100644 layout/generic/crashtests/688996-2.html create mode 100644 layout/generic/crashtests/691210.html create mode 100644 layout/generic/crashtests/700031.xhtml create mode 100644 layout/generic/crashtests/709398-1.html create mode 100644 layout/generic/crashtests/718516.html create mode 100644 layout/generic/crashtests/723108.html create mode 100644 layout/generic/crashtests/724235.html create mode 100644 layout/generic/crashtests/724978.xhtml create mode 100644 layout/generic/crashtests/730559.html create mode 100644 layout/generic/crashtests/734777.html create mode 100644 layout/generic/crashtests/737313-1.html create mode 100644 layout/generic/crashtests/737313-2.html create mode 100644 layout/generic/crashtests/737313-3.html create mode 100644 layout/generic/crashtests/740199-1.xhtml create mode 100644 layout/generic/crashtests/742602.html create mode 100644 layout/generic/crashtests/743364.html create mode 100644 layout/generic/crashtests/747688.html create mode 100644 layout/generic/crashtests/750066-iframe.html create mode 100644 layout/generic/crashtests/750066.html create mode 100644 layout/generic/crashtests/757413-2.html create mode 100644 layout/generic/crashtests/757413.xhtml create mode 100644 layout/generic/crashtests/762764-1.html create mode 100644 layout/generic/crashtests/762902.html create mode 100644 layout/generic/crashtests/765409.html create mode 100644 layout/generic/crashtests/765621.html create mode 100644 layout/generic/crashtests/767765.html create mode 100644 layout/generic/crashtests/769120.html create mode 100644 layout/generic/crashtests/769303-1.html create mode 100644 layout/generic/crashtests/769303-2.html create mode 100644 layout/generic/crashtests/777838.html create mode 100644 layout/generic/crashtests/783228.html create mode 100644 layout/generic/crashtests/784600.html create mode 100644 layout/generic/crashtests/785555.html create mode 100644 layout/generic/crashtests/786740-1.html create mode 100644 layout/generic/crashtests/790252-1.html create mode 100644 layout/generic/crashtests/790252-2.html create mode 100644 layout/generic/crashtests/790260-1.html create mode 100644 layout/generic/crashtests/791601.xhtml create mode 100644 layout/generic/crashtests/794693.html create mode 100644 layout/generic/crashtests/798020-1.html create mode 100644 layout/generic/crashtests/798235-1.html create mode 100644 layout/generic/crashtests/799207-1.html create mode 100644 layout/generic/crashtests/799207-2.html create mode 100644 layout/generic/crashtests/801268-1.html create mode 100644 layout/generic/crashtests/804089-1.xhtml create mode 100644 layout/generic/crashtests/807565-1.html create mode 100644 layout/generic/crashtests/807565-2.html create mode 100644 layout/generic/crashtests/810303.html create mode 100644 layout/generic/crashtests/810726-2.html create mode 100644 layout/generic/crashtests/810726.html create mode 100644 layout/generic/crashtests/812822-1.html create mode 100644 layout/generic/crashtests/812879-1.html create mode 100644 layout/generic/crashtests/812879-2.html create mode 100644 layout/generic/crashtests/812893.html create mode 100644 layout/generic/crashtests/814995.html create mode 100644 layout/generic/crashtests/822910.xhtml create mode 100644 layout/generic/crashtests/824297-1.html create mode 100644 layout/generic/crashtests/825810-1.html create mode 100644 layout/generic/crashtests/825810-2.html create mode 100644 layout/generic/crashtests/826483-1.html create mode 100644 layout/generic/crashtests/826532-1.html create mode 100644 layout/generic/crashtests/827076.html create mode 100644 layout/generic/crashtests/827168-1.html create mode 100644 layout/generic/crashtests/836895.html create mode 100644 layout/generic/crashtests/837007.xhtml create mode 100644 layout/generic/crashtests/840787.html create mode 100644 layout/generic/crashtests/840818.html create mode 100644 layout/generic/crashtests/842132-1.html create mode 100644 layout/generic/crashtests/842166.html create mode 100644 layout/generic/crashtests/844529-1.html create mode 100644 layout/generic/crashtests/847130.xhtml create mode 100644 layout/generic/crashtests/847208.html create mode 100644 layout/generic/crashtests/847209.html create mode 100644 layout/generic/crashtests/847211-1.html create mode 100644 layout/generic/crashtests/849603.html create mode 100644 layout/generic/crashtests/849987.html create mode 100644 layout/generic/crashtests/850931.html create mode 100644 layout/generic/crashtests/851396-1.html create mode 100644 layout/generic/crashtests/854263-1.html create mode 100644 layout/generic/crashtests/862185.html create mode 100644 layout/generic/crashtests/863935.html create mode 100644 layout/generic/crashtests/866547-1.html create mode 100644 layout/generic/crashtests/866767-1.html create mode 100644 layout/generic/crashtests/868906.html create mode 100644 layout/generic/crashtests/876074-1.html create mode 100644 layout/generic/crashtests/876155.html create mode 100644 layout/generic/crashtests/883514-1.html create mode 100644 layout/generic/crashtests/883514-2.html create mode 100644 layout/generic/crashtests/885009-1.html create mode 100644 layout/generic/crashtests/893496-1.html create mode 100644 layout/generic/crashtests/893523.html create mode 100644 layout/generic/crashtests/898871-iframe.xhtml create mode 100644 layout/generic/crashtests/898871.html create mode 100644 layout/generic/crashtests/898871.jpg create mode 100644 layout/generic/crashtests/914501.html create mode 100644 layout/generic/crashtests/914891.html create mode 100644 layout/generic/crashtests/915475.xhtml create mode 100644 layout/generic/crashtests/927558.html create mode 100644 layout/generic/crashtests/942794-1.html create mode 100644 layout/generic/crashtests/943509-1.html create mode 100644 layout/generic/crashtests/944909-1.html create mode 100644 layout/generic/crashtests/946167-1.html create mode 100644 layout/generic/crashtests/947158-iframe.html create mode 100644 layout/generic/crashtests/947158.html create mode 100644 layout/generic/crashtests/949932.html create mode 100644 layout/generic/crashtests/961859.html create mode 100644 layout/generic/crashtests/963878.html create mode 100644 layout/generic/crashtests/964078.html create mode 100644 layout/generic/crashtests/970710.html create mode 100644 layout/generic/crashtests/973701-1.xhtml create mode 100644 layout/generic/crashtests/973701-2.xhtml create mode 100644 layout/generic/crashtests/986899.html create mode 100644 layout/generic/crashtests/crashtests.list create mode 100644 layout/generic/crashtests/details-containing-only-text.html create mode 100644 layout/generic/crashtests/details-display-none-summary-1.html create mode 100644 layout/generic/crashtests/details-display-none-summary-2.html create mode 100644 layout/generic/crashtests/details-display-none-summary-3.html create mode 100644 layout/generic/crashtests/details-open-overflow-auto.html create mode 100644 layout/generic/crashtests/details-open-overflow-hidden.html create mode 100644 layout/generic/crashtests/details-three-columns.html create mode 100644 layout/generic/crashtests/empty.html create mode 100644 layout/generic/crashtests/file_324318-1.html create mode 100644 layout/generic/crashtests/first-letter-638937-1.html create mode 100644 layout/generic/crashtests/first-letter-638937-2.html create mode 100644 layout/generic/crashtests/flex-nested-abspos-1.html create mode 100644 layout/generic/crashtests/font-inflation-762332.html create mode 100644 layout/generic/crashtests/image.jpg create mode 100644 layout/generic/crashtests/large-border-radius-dashed.html create mode 100644 layout/generic/crashtests/large-border-radius-dashed2.html create mode 100644 layout/generic/crashtests/large-border-radius-dotted.html create mode 100644 layout/generic/crashtests/large-border-radius-dotted2.html create mode 100644 layout/generic/crashtests/outline-on-frameset.xhtml create mode 100644 layout/generic/crashtests/simple_blank.swf create mode 100644 layout/generic/crashtests/solidblue.png create mode 100644 layout/generic/crashtests/summary-position-out-of-flow.html create mode 100644 layout/generic/crashtests/text-overflow-bug666751-1.html create mode 100644 layout/generic/crashtests/text-overflow-bug666751-2.html create mode 100644 layout/generic/crashtests/text-overflow-bug670564.xhtml create mode 100644 layout/generic/crashtests/text-overflow-bug671796.xhtml create mode 100644 layout/generic/crashtests/text-overflow-bug713610.html create mode 100644 layout/generic/crashtests/text-overflow-form-elements.html create mode 100644 layout/generic/crashtests/text-overflow-iframe.html create mode 100644 layout/generic/folder.png create mode 100644 layout/generic/frame-graph.py create mode 100644 layout/generic/jar.mn create mode 100644 layout/generic/moz.build create mode 100644 layout/generic/nsAbsoluteContainingBlock.cpp create mode 100644 layout/generic/nsAbsoluteContainingBlock.h create mode 100644 layout/generic/nsAtomicContainerFrame.h create mode 100644 layout/generic/nsBackdropFrame.cpp create mode 100644 layout/generic/nsBackdropFrame.h create mode 100644 layout/generic/nsBlockDebugFlags.h create mode 100644 layout/generic/nsBlockFrame.cpp create mode 100644 layout/generic/nsBlockFrame.h create mode 100644 layout/generic/nsBlockReflowContext.cpp create mode 100644 layout/generic/nsBlockReflowContext.h create mode 100644 layout/generic/nsCanvasFrame.cpp create mode 100644 layout/generic/nsCanvasFrame.h create mode 100644 layout/generic/nsColumnSetFrame.cpp create mode 100644 layout/generic/nsColumnSetFrame.h create mode 100644 layout/generic/nsContainerFrame.cpp create mode 100644 layout/generic/nsContainerFrame.h create mode 100644 layout/generic/nsContainerFrameInlines.h create mode 100644 layout/generic/nsDirection.h create mode 100644 layout/generic/nsFirstLetterFrame.cpp create mode 100644 layout/generic/nsFirstLetterFrame.h create mode 100644 layout/generic/nsFlexContainerFrame.cpp create mode 100644 layout/generic/nsFlexContainerFrame.h create mode 100644 layout/generic/nsFloatManager.cpp create mode 100644 layout/generic/nsFloatManager.h create mode 100644 layout/generic/nsFontInflationData.cpp create mode 100644 layout/generic/nsFontInflationData.h create mode 100644 layout/generic/nsFrameList.cpp create mode 100644 layout/generic/nsFrameList.h create mode 100644 layout/generic/nsFrameSelection.cpp create mode 100644 layout/generic/nsFrameSelection.h create mode 100644 layout/generic/nsFrameSetFrame.cpp create mode 100644 layout/generic/nsFrameSetFrame.h create mode 100644 layout/generic/nsFrameState.cpp create mode 100644 layout/generic/nsFrameState.h create mode 100644 layout/generic/nsFrameStateBits.h create mode 100644 layout/generic/nsGfxScrollFrame.cpp create mode 100644 layout/generic/nsGfxScrollFrame.h create mode 100644 layout/generic/nsGridContainerFrame.cpp create mode 100644 layout/generic/nsGridContainerFrame.h create mode 100644 layout/generic/nsHTMLCanvasFrame.cpp create mode 100644 layout/generic/nsHTMLCanvasFrame.h create mode 100644 layout/generic/nsHTMLParts.h create mode 100644 layout/generic/nsIAnonymousContentCreator.h create mode 100644 layout/generic/nsIFrame.cpp create mode 100644 layout/generic/nsIFrame.h create mode 100644 layout/generic/nsIFrameInlines.h create mode 100644 layout/generic/nsILineIterator.cpp create mode 100644 layout/generic/nsILineIterator.h create mode 100644 layout/generic/nsIScrollPositionListener.h create mode 100644 layout/generic/nsIScrollableFrame.h create mode 100644 layout/generic/nsIStatefulFrame.h create mode 100644 layout/generic/nsImageFrame.cpp create mode 100644 layout/generic/nsImageFrame.h create mode 100644 layout/generic/nsImageMap.cpp create mode 100644 layout/generic/nsImageMap.h create mode 100644 layout/generic/nsInlineFrame.cpp create mode 100644 layout/generic/nsInlineFrame.h create mode 100644 layout/generic/nsIntervalSet.cpp create mode 100644 layout/generic/nsIntervalSet.h create mode 100644 layout/generic/nsLeafFrame.cpp create mode 100644 layout/generic/nsLeafFrame.h create mode 100644 layout/generic/nsLineBox.cpp create mode 100644 layout/generic/nsLineBox.h create mode 100644 layout/generic/nsLineLayout.cpp create mode 100644 layout/generic/nsLineLayout.h create mode 100644 layout/generic/nsPageContentFrame.cpp create mode 100644 layout/generic/nsPageContentFrame.h create mode 100644 layout/generic/nsPageFrame.cpp create mode 100644 layout/generic/nsPageFrame.h create mode 100644 layout/generic/nsPageSequenceFrame.cpp create mode 100644 layout/generic/nsPageSequenceFrame.h create mode 100644 layout/generic/nsPlaceholderFrame.cpp create mode 100644 layout/generic/nsPlaceholderFrame.h create mode 100644 layout/generic/nsQueryFrame.h create mode 100644 layout/generic/nsRubyBaseContainerFrame.cpp create mode 100644 layout/generic/nsRubyBaseContainerFrame.h create mode 100644 layout/generic/nsRubyBaseFrame.cpp create mode 100644 layout/generic/nsRubyBaseFrame.h create mode 100644 layout/generic/nsRubyContentFrame.cpp create mode 100644 layout/generic/nsRubyContentFrame.h create mode 100644 layout/generic/nsRubyFrame.cpp create mode 100644 layout/generic/nsRubyFrame.h create mode 100644 layout/generic/nsRubyTextContainerFrame.cpp create mode 100644 layout/generic/nsRubyTextContainerFrame.h create mode 100644 layout/generic/nsRubyTextFrame.cpp create mode 100644 layout/generic/nsRubyTextFrame.h create mode 100644 layout/generic/nsSplittableFrame.cpp create mode 100644 layout/generic/nsSplittableFrame.h create mode 100644 layout/generic/nsSubDocumentFrame.cpp create mode 100644 layout/generic/nsSubDocumentFrame.h create mode 100644 layout/generic/nsTextFrame.cpp create mode 100644 layout/generic/nsTextFrame.h create mode 100644 layout/generic/nsTextFrameUtils.cpp create mode 100644 layout/generic/nsTextFrameUtils.h create mode 100644 layout/generic/nsTextPaintStyle.cpp create mode 100644 layout/generic/nsTextPaintStyle.h create mode 100644 layout/generic/nsTextRunTransformations.cpp create mode 100644 layout/generic/nsTextRunTransformations.h create mode 100644 layout/generic/nsVideoFrame.cpp create mode 100644 layout/generic/nsVideoFrame.h create mode 100644 layout/generic/test/bug1174521.html create mode 100644 layout/generic/test/bug344830_testembed.svg create mode 100644 layout/generic/test/bug421839-2-page.html create mode 100644 layout/generic/test/bug633762_iframe.html create mode 100644 layout/generic/test/chrome.ini create mode 100644 layout/generic/test/file_BrokenImageReference.png create mode 100644 layout/generic/test/file_Dolske.png create mode 100644 layout/generic/test/file_IconTestServer.sjs create mode 100644 layout/generic/test/file_LoadingImageReference.png create mode 100644 layout/generic/test/file_SlowImage.sjs create mode 100644 layout/generic/test/file_SlowPage.sjs create mode 100644 layout/generic/test/file_SlowTallImage.sjs create mode 100644 layout/generic/test/file_bug1307853.html create mode 100644 layout/generic/test/file_bug1566783.html create mode 100644 layout/generic/test/file_bug448987.html create mode 100644 layout/generic/test/file_bug448987_notref.html create mode 100644 layout/generic/test/file_bug448987_ref.html create mode 100644 layout/generic/test/file_bug449653_1.html create mode 100644 layout/generic/test/file_bug449653_1_ref.html create mode 100644 layout/generic/test/file_bug514732_window.xhtml create mode 100644 layout/generic/test/file_bug579767_1.html create mode 100644 layout/generic/test/file_bug579767_2.html create mode 100644 layout/generic/test/file_reframe_for_lazy_load_image.html create mode 100644 layout/generic/test/file_scroll_position_restore.html create mode 100644 layout/generic/test/file_scroll_position_restore_no_bfcache.html create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-1.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-2.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-3.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-tainted-ref.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-1.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-2.svg create mode 100644 layout/generic/test/file_taintedfilters_feDisplacementMap-untainted-ref.svg create mode 100644 layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg create mode 100644 layout/generic/test/file_taintedfilters_red-flood-for-feImage-cors.svg^headers^ create mode 100644 layout/generic/test/file_taintedfilters_red-flood-for-feImage.svg create mode 100644 layout/generic/test/frame_selection_underline-ref.xhtml create mode 100644 layout/generic/test/frame_selection_underline.css create mode 100644 layout/generic/test/frame_selection_underline.xhtml create mode 100644 layout/generic/test/frame_visibility_in_iframe.html create mode 100644 layout/generic/test/frame_visibility_in_iframe_child.html create mode 100644 layout/generic/test/mochitest.ini create mode 100644 layout/generic/test/page_scroll_with_fixed_pos_window.html create mode 100644 layout/generic/test/slow-stylesheet.sjs create mode 100644 layout/generic/test/test_backspace_delete.xhtml create mode 100644 layout/generic/test/test_bug1062406.html create mode 100644 layout/generic/test/test_bug1174521.html create mode 100644 layout/generic/test/test_bug1198135.html create mode 100644 layout/generic/test/test_bug1307853.html create mode 100644 layout/generic/test/test_bug1408607.html create mode 100644 layout/generic/test/test_bug1499961.html create mode 100644 layout/generic/test/test_bug1566783.html create mode 100644 layout/generic/test/test_bug1623764.html create mode 100644 layout/generic/test/test_bug1642588.html create mode 100644 layout/generic/test/test_bug1644511.html create mode 100644 layout/generic/test/test_bug1655135.html create mode 100644 layout/generic/test/test_bug1756831.html create mode 100644 layout/generic/test/test_bug1803209.html create mode 100644 layout/generic/test/test_bug240933.html create mode 100644 layout/generic/test/test_bug263683.html create mode 100644 layout/generic/test/test_bug288789.html create mode 100644 layout/generic/test/test_bug290397.html create mode 100644 layout/generic/test/test_bug323656.html create mode 100644 layout/generic/test/test_bug344830.html create mode 100644 layout/generic/test/test_bug348681.html create mode 100644 layout/generic/test/test_bug382429.html create mode 100644 layout/generic/test/test_bug384527.html create mode 100644 layout/generic/test/test_bug385751.html create mode 100644 layout/generic/test/test_bug389630.html create mode 100644 layout/generic/test/test_bug391747.html create mode 100644 layout/generic/test/test_bug392746.html create mode 100644 layout/generic/test/test_bug392923.html create mode 100644 layout/generic/test/test_bug394173.html create mode 100644 layout/generic/test/test_bug394239.html create mode 100644 layout/generic/test/test_bug402380.html create mode 100644 layout/generic/test/test_bug404872.html create mode 100644 layout/generic/test/test_bug405178.html create mode 100644 layout/generic/test/test_bug416168.html create mode 100644 layout/generic/test/test_bug421436.html create mode 100644 layout/generic/test/test_bug421839-1.html create mode 100644 layout/generic/test/test_bug421839-2.html create mode 100644 layout/generic/test/test_bug424627.html create mode 100644 layout/generic/test/test_bug438840.html create mode 100644 layout/generic/test/test_bug448860.html create mode 100644 layout/generic/test/test_bug448987.html create mode 100644 layout/generic/test/test_bug449653.html create mode 100644 layout/generic/test/test_bug460532.html create mode 100644 layout/generic/test/test_bug468167.html create mode 100644 layout/generic/test/test_bug469613.xhtml create mode 100644 layout/generic/test/test_bug469774.xhtml create mode 100644 layout/generic/test/test_bug470212.html create mode 100644 layout/generic/test/test_bug488417.html create mode 100644 layout/generic/test/test_bug496275.html create mode 100644 layout/generic/test/test_bug503813.html create mode 100644 layout/generic/test/test_bug507902.html create mode 100644 layout/generic/test/test_bug508115.xhtml create mode 100644 layout/generic/test/test_bug514732-2.xhtml create mode 100644 layout/generic/test/test_bug522632.html create mode 100644 layout/generic/test/test_bug524925.html create mode 100644 layout/generic/test/test_bug579767.html create mode 100644 layout/generic/test/test_bug589621.html create mode 100644 layout/generic/test/test_bug589623.html create mode 100644 layout/generic/test/test_bug597333.html create mode 100644 layout/generic/test/test_bug632379.xhtml create mode 100644 layout/generic/test/test_bug633762.html create mode 100644 layout/generic/test/test_bug666225.html create mode 100644 layout/generic/test/test_bug719503.html create mode 100644 layout/generic/test/test_bug719515.html create mode 100644 layout/generic/test/test_bug719518.html create mode 100644 layout/generic/test/test_bug719523.html create mode 100644 layout/generic/test/test_bug735641.html create mode 100644 layout/generic/test/test_bug748961.html create mode 100644 layout/generic/test/test_bug756984.html create mode 100644 layout/generic/test/test_bug784410.html create mode 100644 layout/generic/test/test_bug785324.html create mode 100644 layout/generic/test/test_bug791616.html create mode 100644 layout/generic/test/test_bug831780.html create mode 100644 layout/generic/test/test_bug841361.html create mode 100644 layout/generic/test/test_bug904810.html create mode 100644 layout/generic/test/test_bug938772.html create mode 100644 layout/generic/test/test_bug970363.html create mode 100644 layout/generic/test/test_crash_on_mouse_move.html create mode 100644 layout/generic/test/test_dynamic_reflow_root_disallowal.html create mode 100644 layout/generic/test/test_flex_interrupt.html create mode 100644 layout/generic/test/test_frame_visibility_in_iframe.html create mode 100644 layout/generic/test/test_grid_track_sizing_algo_001.html create mode 100644 layout/generic/test/test_grid_track_sizing_algo_002.html create mode 100644 layout/generic/test/test_image_selection.html create mode 100644 layout/generic/test/test_image_selection_2.html create mode 100644 layout/generic/test/test_image_selection_3.html create mode 100644 layout/generic/test/test_image_selection_in_contenteditable.html create mode 100644 layout/generic/test/test_intrinsic_size_on_loading.html create mode 100644 layout/generic/test/test_key_enter_open_second_summary.html create mode 100644 layout/generic/test/test_key_enter_prevent_default.html create mode 100644 layout/generic/test/test_key_enter_single_summary.html create mode 100644 layout/generic/test/test_key_space_single_summary.html create mode 100644 layout/generic/test/test_movement_by_characters.html create mode 100644 layout/generic/test/test_movement_by_words.html create mode 100644 layout/generic/test/test_overflow_event.html create mode 100644 layout/generic/test/test_overlay_scrollbar_position.html create mode 100644 layout/generic/test/test_page_scroll_with_fixed_pos.html create mode 100644 layout/generic/test/test_reframe_for_lazy_load_image.html create mode 100644 layout/generic/test/test_scroll_animation_restore.html create mode 100644 layout/generic/test/test_scroll_behavior.html create mode 100644 layout/generic/test/test_scroll_on_display_contents.html create mode 100644 layout/generic/test/test_scroll_position_iframe.html create mode 100644 layout/generic/test/test_scroll_position_restore.html create mode 100644 layout/generic/test/test_scroll_position_restore_after_stop.html create mode 100644 layout/generic/test/test_scroll_position_restore_no_bfcache.html create mode 100644 layout/generic/test/test_scrollframe_abspos_interrupt.html create mode 100644 layout/generic/test/test_selection_changes_with_middle_mouse_button.html create mode 100644 layout/generic/test/test_selection_doubleclick.html create mode 100644 layout/generic/test/test_selection_expanding.html create mode 100644 layout/generic/test/test_selection_multiclick_drag.html create mode 100644 layout/generic/test/test_selection_preventDefault.html create mode 100644 layout/generic/test/test_selection_splitText-normalize.html create mode 100644 layout/generic/test/test_selection_touchevents.html create mode 100644 layout/generic/test/test_selection_tripleclick.html create mode 100644 layout/generic/test/test_selection_underline.html create mode 100644 layout/generic/test/test_taintedfilters.html (limited to 'layout/generic') diff --git a/layout/generic/AnonymousContentKey.h b/layout/generic/AnonymousContentKey.h new file mode 100644 index 0000000000..531754fd20 --- /dev/null +++ b/layout/generic/AnonymousContentKey.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* values to identify particular subtrees of native anonymous content */ + +#ifndef mozilla_AnonymousContentKey_h +#define mozilla_AnonymousContentKey_h + +#include "mozilla/TypedEnumBits.h" +#include +#include "X11UndefineNone.h" + +namespace mozilla { + +// clang-format off + +// We currently use cached anonymous content styles only for scrollbar parts, +// and we can fit the type of scrollbar part element along with its different +// options (such as orientation, and other attribute values that can affect +// styling) into a uint8_t. +// +// The lower three bits hold a Type_* value identifying the type of +// element, and the remaining bits store Flag_* values. +// +// A value of 0 is used to represent an anonymous content subtree that we don't +// cache styles for. +enum class AnonymousContentKey : uint8_t { + None = 0x00, + + // all + Type_ScrollCorner = 0x01, + Type_Scrollbar = 0x02, + Type_ScrollbarButton = 0x03, + Type_Slider = 0x04, + + // scrollbar, scrollbarbutton, slider + Flag_Vertical = 0x08, + + // scrollbarbutton + Flag_ScrollbarButton_Down = 0x10, + Flag_ScrollbarButton_Bottom = 0x20, + Flag_ScrollbarButton_Decrement = 0x40, +}; + +// clang-format on + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AnonymousContentKey) + +} // namespace mozilla + +#endif // mozilla_AnonymousContentKey_h diff --git a/layout/generic/AspectRatio.cpp b/layout/generic/AspectRatio.cpp new file mode 100644 index 0000000000..2fe51fed5e --- /dev/null +++ b/layout/generic/AspectRatio.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "AspectRatio.h" + +#include "mozilla/WritingModes.h" + +namespace mozilla { + +nscoord AspectRatio::ComputeRatioDependentSize( + LogicalAxis aRatioDependentAxis, const WritingMode& aWM, + nscoord aRatioDeterminingSize, + const LogicalSize& aContentBoxSizeToBoxSizingAdjust) const { + MOZ_ASSERT(*this, + "Infinite or zero ratio may have undefined behavior when " + "computing the size"); + const LogicalSize& boxSizingAdjust = mUseBoxSizing == UseBoxSizing::No + ? LogicalSize(aWM) + : aContentBoxSizeToBoxSizingAdjust; + return aRatioDependentAxis == LogicalAxis::eLogicalAxisInline + ? ConvertToWritingMode(aWM).ApplyTo(aRatioDeterminingSize + + boxSizingAdjust.BSize(aWM)) - + boxSizingAdjust.ISize(aWM) + : ConvertToWritingMode(aWM).Inverted().ApplyTo( + aRatioDeterminingSize + boxSizingAdjust.ISize(aWM)) - + boxSizingAdjust.BSize(aWM); +} + +} // namespace mozilla diff --git a/layout/generic/AspectRatio.h b/layout/generic/AspectRatio.h new file mode 100644 index 0000000000..d2602604cc --- /dev/null +++ b/layout/generic/AspectRatio.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_AspectRatio_h +#define mozilla_AspectRatio_h + +/* The aspect ratio of a box, in a "width / height" format. */ + +#include "mozilla/Attributes.h" +#include "mozilla/gfx/BaseSize.h" +#include "nsCoord.h" +#include +#include + +namespace IPC { +template +struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +enum LogicalAxis : uint8_t; +class LogicalSize; +class WritingMode; + +enum class UseBoxSizing : uint8_t { + // The aspect ratio works with content box dimensions always. + No, + // The aspect ratio works with the dimensions of the box specified by + // box-sizing. + Yes, +}; + +struct AspectRatio { + friend struct IPC::ParamTraits; + + AspectRatio() = default; + explicit AspectRatio(float aRatio, + UseBoxSizing aUseBoxSizing = UseBoxSizing::No) + : mRatio(std::max(aRatio, 0.0f)), mUseBoxSizing(aUseBoxSizing) {} + + static AspectRatio FromSize(float aWidth, float aHeight, + UseBoxSizing aUseBoxSizing = UseBoxSizing::No) { + if (aWidth == 0.0f || aHeight == 0.0f) { + // For the degenerate ratio, we don't care about which box sizing we are + // using, so using default constructor is fine. + return AspectRatio(); + } + return AspectRatio(aWidth / aHeight, aUseBoxSizing); + } + + template + static AspectRatio FromSize(const gfx::BaseSize& aSize) { + return FromSize(aSize.Width(), aSize.Height()); + } + + explicit operator bool() const { return mRatio != 0.0f; } + + nscoord ApplyTo(nscoord aCoord) const { + MOZ_DIAGNOSTIC_ASSERT(*this); + return NSCoordSaturatingNonnegativeMultiply(aCoord, mRatio); + } + + float ApplyToFloat(float aFloat) const { + MOZ_DIAGNOSTIC_ASSERT(*this); + return mRatio * aFloat; + } + + // Inverts the ratio, in order to get the height / width ratio. + [[nodiscard]] AspectRatio Inverted() const { + if (!*this) { + return AspectRatio(); + } + // Clamp to a small epsilon, in case mRatio is absurdly large & produces + // 0.0f in the division here (so that valid ratios always generate other + // valid ratios when inverted). + return AspectRatio( + std::max(std::numeric_limits::epsilon(), 1.0f / mRatio), + mUseBoxSizing); + } + + [[nodiscard]] inline AspectRatio ConvertToWritingMode( + const WritingMode& aWM) const; + + /** + * This method computes the ratio-dependent size by the ratio-determining size + * and aspect-ratio (i.e. preferred aspect ratio). Basically this function + * will be used in the calculation of 'auto' sizes when the preferred + * aspect ratio is not 'auto'. + * + * @param aRatioDependentAxis The ratio depenedent axis of the box. + * @param aWM The writing mode of the box. + * @param aRatioDetermingSize The content-box size on the ratio determining + * axis. Basically, we use this size and |mRatio| + * to compute the size on the ratio-dependent + * axis. + * @param aContentBoxSizeToBoxSizingAdjust The border padding box size + * adjustment. We need this because + * aspect-ratio should take the + * box-sizing into account if its + * style is ''. If its style + * is 'auto & ', we should use + * content-box dimensions always. + * If the callers want the ratio to + * apply to the content-box size, we + * should pass a zero LogicalSize. + * If mUseBoxSizing is No, we ignore + * this parameter because we should + * use content box dimensions always. + * + * The return value is the content-box size on the ratio-dependent axis. + * Plese see the definition of the ratio-dependent axis and the + * ratio-determining axis in the spec: + * https://drafts.csswg.org/css-sizing-4/#aspect-ratio + */ + [[nodiscard]] nscoord ComputeRatioDependentSize( + LogicalAxis aRatioDependentAxis, const WritingMode& aWM, + nscoord aRatioDeterminingSize, + const LogicalSize& aContentBoxSizeToBoxSizingAdjust) const; + + bool operator==(const AspectRatio& aOther) const { + return mRatio == aOther.mRatio && mUseBoxSizing == aOther.mUseBoxSizing; + } + + bool operator!=(const AspectRatio& aOther) const { + return !(*this == aOther); + } + + bool operator<(const AspectRatio& aOther) const { + MOZ_ASSERT( + mUseBoxSizing == aOther.mUseBoxSizing, + "Do not compare AspectRatio if their mUseBoxSizing are different."); + return mRatio < aOther.mRatio; + } + + private: + // 0.0f represents no aspect ratio. + float mRatio = 0.0f; + UseBoxSizing mUseBoxSizing = UseBoxSizing::No; +}; + +} // namespace mozilla + +#endif // mozilla_AspectRatio_h diff --git a/layout/generic/AutoCopyListener.h b/layout/generic/AutoCopyListener.h new file mode 100644 index 0000000000..acb0aaa46d --- /dev/null +++ b/layout/generic/AutoCopyListener.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_AutoCopyListener_h +#define mozilla_AutoCopyListener_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/Selection.h" +#include "mozilla/StaticPrefs_clipboard.h" +#include "nsIClipboard.h" + +namespace mozilla { + +class AutoCopyListener final { + public: + /** + * OnSelectionChange() is called when a Selection whose NotifyAutoCopy() was + * called is changed. + * + * @param aDocument The document of the Selection. May be nullptr. + * @param aSelection The selection. + * @param aReason The reasons of the change. + * See nsISelectionListener::*_REASON. + */ + static void OnSelectionChange(dom::Document* aDocument, + dom::Selection& aSelection, int16_t aReason); + + /** + * Init() initializes all static members of this class. Should be called + * only once. + */ + static void Init(int16_t aClipboardID) { + MOZ_ASSERT(IsValidClipboardID(aClipboardID)); + static bool sInitialized = false; + if (!sInitialized && IsValidClipboardID(aClipboardID)) { + sClipboardID = aClipboardID; + sInitialized = true; + } + } + + /** + * IsPrefEnabled() returns true if the pref enables auto-copy feature. + */ + static bool IsPrefEnabled() { return StaticPrefs::clipboard_autocopy(); } + + private: + static bool IsValidClipboardID(int16_t aClipboardID) { + return aClipboardID >= nsIClipboard::kSelectionClipboard && + aClipboardID <= nsIClipboard::kSelectionCache; + } + + static int16_t sClipboardID; +}; + +} // namespace mozilla + +#endif // #ifndef mozilla_AutoCopyListener_h diff --git a/layout/generic/BRFrame.cpp b/layout/generic/BRFrame.cpp new file mode 100644 index 0000000000..5cb7c104d9 --- /dev/null +++ b/layout/generic/BRFrame.cpp @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* rendering object for HTML
elements */ + +#include "mozilla/PresShell.h" +#include "mozilla/dom/HTMLBRElement.h" +#include "gfxContext.h" +#include "nsCOMPtr.h" +#include "nsContainerFrame.h" +#include "nsFontMetrics.h" +#include "nsHTMLParts.h" +#include "nsIFrame.h" +#include "nsPresContext.h" +#include "nsLineLayout.h" +#include "nsStyleConsts.h" +#include "nsGkAtoms.h" +#include "nsLayoutUtils.h" + +// FOR SELECTION +#include "nsIContent.h" +// END INCLUDES FOR SELECTION + +using namespace mozilla; + +namespace mozilla { + +class BRFrame final : public nsIFrame { + public: + NS_DECL_FRAMEARENA_HELPERS(BRFrame) + + friend nsIFrame* ::NS_NewBRFrame(mozilla::PresShell* aPresShell, + ComputedStyle* aStyle); + + ContentOffsets CalcContentOffsetsFromFramePoint( + const nsPoint& aPoint) override; + + FrameSearchResult PeekOffsetNoAmount(bool aForward, + int32_t* aOffset) override; + FrameSearchResult PeekOffsetCharacter( + bool aForward, int32_t* aOffset, + PeekOffsetCharacterOptions aOptions = + PeekOffsetCharacterOptions()) override; + FrameSearchResult PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, + bool aIsKeyboardSelect, int32_t* aOffset, + PeekWordState* aState, + bool aTrimSpaces) override; + + void Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; + void AddInlineMinISize(gfxContext* aRenderingContext, + InlineMinISizeData* aData) override; + void AddInlinePrefISize(gfxContext* aRenderingContext, + InlinePrefISizeData* aData) override; + nscoord GetMinISize(gfxContext* aRenderingContext) override; + nscoord GetPrefISize(gfxContext* aRenderingContext) override; + + Maybe GetNaturalBaselineBOffset( + WritingMode aWM, BaselineSharingGroup aBaselineGroup, + BaselineExportContext) const override; + + bool IsFrameOfType(uint32_t aFlags) const override { + return nsIFrame::IsFrameOfType( + aFlags & ~(nsIFrame::eReplaced | nsIFrame::eLineParticipant)); + } + +#ifdef ACCESSIBILITY + mozilla::a11y::AccType AccessibleType() override; +#endif + +#ifdef DEBUG_FRAME_DUMP + nsresult GetFrameName(nsAString& aResult) const override { + return MakeFrameName(u"BR"_ns, aResult); + } +#endif + + protected: + BRFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) + : nsIFrame(aStyle, aPresContext, kClassID), + mAscent(NS_INTRINSIC_ISIZE_UNKNOWN) {} + + virtual ~BRFrame(); + + nscoord mAscent; +}; + +} // namespace mozilla + +nsIFrame* NS_NewBRFrame(mozilla::PresShell* aPresShell, ComputedStyle* aStyle) { + return new (aPresShell) BRFrame(aStyle, aPresShell->GetPresContext()); +} + +NS_IMPL_FRAMEARENA_HELPERS(BRFrame) + +BRFrame::~BRFrame() = default; + +void BRFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, + const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { + MarkInReflow(); + DO_GLOBAL_REFLOW_COUNT("BRFrame"); + DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); + MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); + + WritingMode wm = aReflowInput.GetWritingMode(); + LogicalSize finalSize(wm); + finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks + // mode by nsLineLayout::VerticalAlignFrames . + // However, it's not always 0. See below. + finalSize.ISize(wm) = 0; + aMetrics.SetBlockStartAscent(0); + + // Only when the BR is operating in a line-layout situation will it + // behave like a BR. Additionally, we suppress breaks from BR inside + // of ruby frames. To determine if we're inside ruby, we have to rely + // on the *parent's* ShouldSuppressLineBreak() method, instead of our + // own, because we may have custom "display" value that makes our + // ShouldSuppressLineBreak() return false. + nsLineLayout* ll = aReflowInput.mLineLayout; + if (ll && !GetParent()->Style()->ShouldSuppressLineBreak()) { + // Note that the compatibility mode check excludes AlmostStandards + // mode, since this is the inline box model. See bug 161691. + if (ll->LineIsEmpty() || + aPresContext->CompatibilityMode() == eCompatibility_FullStandards) { + // The line is logically empty; any whitespace is trimmed away. + // + // If this frame is going to terminate the line we know + // that nothing else will go on the line. Therefore, in this + // case, we provide some height for the BR frame so that it + // creates some vertical whitespace. It's necessary to use the + // line-height rather than the font size because the + // quirks-mode fix that doesn't apply the block's min + // line-height makes this necessary to make BR cause a line + // of the full line-height + + // We also do this in strict mode because BR should act like a + // normal inline frame. That line-height is used is important + // here for cases where the line-height is less than 1. + RefPtr fm = + nsLayoutUtils::GetInflatedFontMetricsForFrame(this); + if (fm) { + nscoord logicalHeight = aReflowInput.GetLineHeight(); + finalSize.BSize(wm) = logicalHeight; + aMetrics.SetBlockStartAscent(nsLayoutUtils::GetCenteredFontBaseline( + fm, logicalHeight, wm.IsLineInverted())); + } else { + aMetrics.SetBlockStartAscent(aMetrics.BSize(wm) = 0); + } + + // XXX temporary until I figure out a better solution; see the + // code in nsLineLayout::VerticalAlignFrames that zaps minY/maxY + // if the width is zero. + // XXX This also fixes bug 10036! + // Warning: nsTextControlFrame::CalculateSizeStandard depends on + // the following line, see bug 228752. + // The code below in AddInlinePrefISize also adds 1 appunit to width + finalSize.ISize(wm) = 1; + } + + // Return our reflow status + aStatus.SetInlineLineBreakAfter(aReflowInput.mStyleDisplay->mClear); + ll->SetLineEndsInBR(true); + } + + aMetrics.SetSize(wm, finalSize); + aMetrics.SetOverflowAreasToDesiredBounds(); + + mAscent = aMetrics.BlockStartAscent(); +} + +/* virtual */ +void BRFrame::AddInlineMinISize(gfxContext* aRenderingContext, + nsIFrame::InlineMinISizeData* aData) { + if (!GetParent()->Style()->ShouldSuppressLineBreak()) { + aData->ForceBreak(); + } +} + +/* virtual */ +void BRFrame::AddInlinePrefISize(gfxContext* aRenderingContext, + nsIFrame::InlinePrefISizeData* aData) { + if (!GetParent()->Style()->ShouldSuppressLineBreak()) { + // Match the 1 appunit width assigned in the Reflow method above + aData->mCurrentLine += 1; + aData->ForceBreak(); + } +} + +/* virtual */ +nscoord BRFrame::GetMinISize(gfxContext* aRenderingContext) { + nscoord result = 0; + DISPLAY_MIN_INLINE_SIZE(this, result); + return result; +} + +/* virtual */ +nscoord BRFrame::GetPrefISize(gfxContext* aRenderingContext) { + nscoord result = 0; + DISPLAY_PREF_INLINE_SIZE(this, result); + return result; +} + +Maybe BRFrame::GetNaturalBaselineBOffset( + WritingMode aWM, BaselineSharingGroup aBaselineGroup, + BaselineExportContext) const { + if (aBaselineGroup == BaselineSharingGroup::Last) { + return Nothing{}; + } + return Some(mAscent); +} + +nsIFrame::ContentOffsets BRFrame::CalcContentOffsetsFromFramePoint( + const nsPoint& aPoint) { + ContentOffsets offsets; + offsets.content = mContent->GetParent(); + if (offsets.content) { + offsets.offset = offsets.content->ComputeIndexOf_Deprecated(mContent); + offsets.secondaryOffset = offsets.offset; + offsets.associate = CARET_ASSOCIATE_AFTER; + } + return offsets; +} + +nsIFrame::FrameSearchResult BRFrame::PeekOffsetNoAmount(bool aForward, + int32_t* aOffset) { + NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range"); + int32_t startOffset = *aOffset; + // If we hit the end of a BR going backwards, go to its beginning and stay + // there. + if (!aForward && startOffset != 0) { + *aOffset = 0; + return FOUND; + } + // Otherwise, stop if we hit the beginning, continue (forward) if we hit the + // end. + return (startOffset == 0) ? FOUND : CONTINUE; +} + +nsIFrame::FrameSearchResult BRFrame::PeekOffsetCharacter( + bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) { + NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range"); + // Keep going. The actual line jumping will stop us. + return CONTINUE; +} + +nsIFrame::FrameSearchResult BRFrame::PeekOffsetWord( + bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect, + int32_t* aOffset, PeekWordState* aState, bool aTrimSpaces) { + NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range"); + // Keep going. The actual line jumping will stop us. + return CONTINUE; +} + +#ifdef ACCESSIBILITY +a11y::AccType BRFrame::AccessibleType() { + dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(mContent); + if (brElement->IsPaddingForEmptyEditor() || + brElement->IsPaddingForEmptyLastLine()) { + // This
is a "padding
element" used when there is no text or an + // empty last line in an editor. + return a11y::eNoType; + } + + return a11y::eHTMLBRType; +} +#endif diff --git a/layout/generic/BlockReflowState.cpp b/layout/generic/BlockReflowState.cpp new file mode 100644 index 0000000000..0060487c9e --- /dev/null +++ b/layout/generic/BlockReflowState.cpp @@ -0,0 +1,1090 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* state used in reflow of block frames */ + +#include "BlockReflowState.h" + +#include +#include "LayoutLogging.h" +#include "nsBlockFrame.h" +#include "nsLineLayout.h" +#include "nsPresContext.h" +#include "nsIFrameInlines.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_layout.h" +#include "TextOverflow.h" + +#ifdef DEBUG +# include "nsBlockDebugFlags.h" +#endif + +using namespace mozilla; +using namespace mozilla::layout; + +BlockReflowState::BlockReflowState(const ReflowInput& aReflowInput, + nsPresContext* aPresContext, + nsBlockFrame* aFrame, bool aBStartMarginRoot, + bool aBEndMarginRoot, + bool aBlockNeedsFloatManager, + const nscoord aConsumedBSize, + const nscoord aEffectiveContentBoxBSize) + : mBlock(aFrame), + mPresContext(aPresContext), + mReflowInput(aReflowInput), + mContentArea(aReflowInput.GetWritingMode()), + mPushedFloats(nullptr), + mOverflowTracker(nullptr), + mBorderPadding( + mReflowInput + .ComputedLogicalBorderPadding(mReflowInput.GetWritingMode()) + .ApplySkipSides(aFrame->PreReflowBlockLevelLogicalSkipSides())), + mPrevBEndMargin(), + mMinLineHeight(aReflowInput.GetLineHeight()), + mLineNumber(0), + mTrailingClearFromPIF(StyleClear::None), + mConsumedBSize(aConsumedBSize) { + NS_ASSERTION(mConsumedBSize != NS_UNCONSTRAINEDSIZE, + "The consumed block-size should be constrained!"); + + WritingMode wm = aReflowInput.GetWritingMode(); + + // Note that mContainerSize is the physical size, needed to + // convert logical block-coordinates in vertical-rl writing mode + // (measured from a RHS origin) to physical coordinates within the + // containing block. + // If aReflowInput doesn't have a constrained ComputedWidth(), we set + // mContainerSize.width to zero, which means lines will be positioned + // (physically) incorrectly; we will fix them up at the end of + // nsBlockFrame::Reflow, after we know the total block-size of the + // frame. + mContainerSize.width = aReflowInput.ComputedWidth(); + if (mContainerSize.width == NS_UNCONSTRAINEDSIZE) { + mContainerSize.width = 0; + } + + mContainerSize.width += mBorderPadding.LeftRight(wm); + + // For now at least, we don't do that fix-up for mContainerHeight. + // It's only used in nsBidiUtils::ReorderFrames for vertical rtl + // writing modes, which aren't fully supported for the time being. + mContainerSize.height = + aReflowInput.ComputedHeight() + mBorderPadding.TopBottom(wm); + + if (aBStartMarginRoot || 0 != mBorderPadding.BStart(wm)) { + mFlags.mIsBStartMarginRoot = true; + mFlags.mShouldApplyBStartMargin = true; + } + if (aBEndMarginRoot || 0 != mBorderPadding.BEnd(wm)) { + mFlags.mIsBEndMarginRoot = true; + } + if (aBlockNeedsFloatManager) { + mFlags.mBlockNeedsFloatManager = true; + } + + mFlags.mCanHaveOverflowMarkers = css::TextOverflow::CanHaveOverflowMarkers( + mBlock, css::TextOverflow::BeforeReflow::Yes); + + MOZ_ASSERT(FloatManager(), + "Float manager should be valid when creating BlockReflowState!"); + + // Save the coordinate system origin for later. + FloatManager()->GetTranslation(mFloatManagerI, mFloatManagerB); + FloatManager()->PushState(&mFloatManagerStateBefore); // never popped + + mNextInFlow = static_cast(mBlock->GetNextInFlow()); + + LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedISize(), + "have unconstrained width; this should only result " + "from very large sizes, not attempts at intrinsic " + "width calculation"); + mContentArea.ISize(wm) = aReflowInput.ComputedISize(); + + // Compute content area block-size. Unlike the inline-size, if we have a + // specified style block-size, we ignore it since extra content is managed by + // the "overflow" property. When we don't have a specified style block-size, + // then we may end up limiting our block-size if the available block-size is + // constrained (this situation occurs when we are paginated). + if (const nscoord availableBSize = aReflowInput.AvailableBSize(); + availableBSize != NS_UNCONSTRAINEDSIZE) { + // We are in a paginated situation. The block-end edge of the available + // space to reflow the children is within our block-end border and padding. + // If we're cloning our border and padding, and we're going to request + // additional continuations because of our excessive content-box block-size, + // then reserve some of our available space for our (cloned) block-end + // border and padding. + const bool reserveSpaceForBlockEndBP = + mReflowInput.mStyleBorder->mBoxDecorationBreak == + StyleBoxDecorationBreak::Clone && + (aEffectiveContentBoxBSize == NS_UNCONSTRAINEDSIZE || + aEffectiveContentBoxBSize + mBorderPadding.BStartEnd(wm) > + availableBSize); + const nscoord bp = reserveSpaceForBlockEndBP ? mBorderPadding.BStartEnd(wm) + : mBorderPadding.BStart(wm); + mContentArea.BSize(wm) = std::max(0, availableBSize - bp); + } else { + // When we are not in a paginated situation, then we always use a + // unconstrained block-size. + mContentArea.BSize(wm) = NS_UNCONSTRAINEDSIZE; + } + mContentArea.IStart(wm) = mBorderPadding.IStart(wm); + mBCoord = mContentArea.BStart(wm) = mBorderPadding.BStart(wm); + + mPrevChild = nullptr; + mCurrentLine = aFrame->LinesEnd(); +} + +void BlockReflowState::ComputeFloatAvoidingOffsets( + nsIFrame* aFloatAvoidingBlock, const LogicalRect& aFloatAvailableSpace, + nscoord& aIStartResult, nscoord& aIEndResult) const { + WritingMode wm = mReflowInput.GetWritingMode(); + // The frame is clueless about the float manager and therefore we + // only give it free space. An example is a table frame - the + // tables do not flow around floats. + // However, we can let its margins intersect floats. + NS_ASSERTION(aFloatAvailableSpace.IStart(wm) >= mContentArea.IStart(wm), + "bad avail space rect inline-coord"); + NS_ASSERTION(aFloatAvailableSpace.ISize(wm) == 0 || + aFloatAvailableSpace.IEnd(wm) <= mContentArea.IEnd(wm), + "bad avail space rect inline-size"); + + nscoord iStartOffset, iEndOffset; + if (aFloatAvailableSpace.ISize(wm) == mContentArea.ISize(wm)) { + // We don't need to compute margins when there are no floats around. + iStartOffset = 0; + iEndOffset = 0; + } else { + const LogicalMargin frameMargin = + SizeComputationInput(aFloatAvoidingBlock, + mReflowInput.mRenderingContext, wm, + mContentArea.ISize(wm)) + .ComputedLogicalMargin(wm); + + nscoord iStartFloatIOffset = + aFloatAvailableSpace.IStart(wm) - mContentArea.IStart(wm); + iStartOffset = std::max(iStartFloatIOffset, frameMargin.IStart(wm)) - + frameMargin.IStart(wm); + iStartOffset = std::max(iStartOffset, 0); // in case of negative margin + nscoord iEndFloatIOffset = + mContentArea.IEnd(wm) - aFloatAvailableSpace.IEnd(wm); + iEndOffset = + std::max(iEndFloatIOffset, frameMargin.IEnd(wm)) - frameMargin.IEnd(wm); + iEndOffset = std::max(iEndOffset, 0); // in case of negative margin + } + aIStartResult = iStartOffset; + aIEndResult = iEndOffset; +} + +LogicalRect BlockReflowState::ComputeBlockAvailSpace( + nsIFrame* aFrame, const nsFlowAreaRect& aFloatAvailableSpace, + bool aBlockAvoidsFloats) { +#ifdef REALLY_NOISY_REFLOW + printf("CBAS frame=%p has floats %d\n", aFrame, + aFloatAvailableSpace.HasFloats()); +#endif + WritingMode wm = mReflowInput.GetWritingMode(); + LogicalRect result(wm); + result.BStart(wm) = mBCoord; + // Note: ContentBSize() and ContentBEnd() are not our content-box size and its + // block-end edge. They really mean "the available block-size for children", + // and "the block-end edge of the available space for children". + result.BSize(wm) = ContentBSize() == NS_UNCONSTRAINEDSIZE + ? NS_UNCONSTRAINEDSIZE + : ContentBEnd() - mBCoord; + // mBCoord might be greater than ContentBEnd() if the block's top margin + // pushes it off the page/column. Negative available block-size can confuse + // other code and is nonsense in principle. + + // XXX Do we really want this condition to be this restrictive (i.e., + // more restrictive than it used to be)? The |else| here is allowed + // by the CSS spec, but only out of desperation given implementations, + // and the behavior it leads to is quite undesirable (it can cause + // things to become extremely narrow when they'd fit quite well a + // little bit lower). Should the else be a quirk or something that + // applies to a specific set of frame classes and no new ones? + // If we did that, then for those frames where the condition below is + // true but nsBlockFrame::BlockCanIntersectFloats is false, + // nsBlockFrame::ISizeToClearPastFloats would need to use the + // shrink-wrap formula, max(MinISize, min(avail width, PrefISize)) + // rather than just using MinISize. + NS_ASSERTION( + nsBlockFrame::BlockCanIntersectFloats(aFrame) == !aBlockAvoidsFloats, + "unexpected replaced width"); + if (!aBlockAvoidsFloats) { + if (aFloatAvailableSpace.HasFloats()) { + // Use the float-edge property to determine how the child block + // will interact with the float. + const nsStyleBorder* borderStyle = aFrame->StyleBorder(); + switch (borderStyle->mFloatEdge) { + default: + case StyleFloatEdge::ContentBox: // content and only content does + // runaround of floats + // The child block will flow around the float. Therefore + // give it all of the available space. + result.IStart(wm) = mContentArea.IStart(wm); + result.ISize(wm) = mContentArea.ISize(wm); + break; + case StyleFloatEdge::MarginBox: { + // The child block's margins should be placed adjacent to, + // but not overlap the float. + result.IStart(wm) = aFloatAvailableSpace.mRect.IStart(wm); + result.ISize(wm) = aFloatAvailableSpace.mRect.ISize(wm); + } break; + } + } else { + // Since there are no floats present the float-edge property + // doesn't matter therefore give the block element all of the + // available space since it will flow around the float itself. + result.IStart(wm) = mContentArea.IStart(wm); + result.ISize(wm) = mContentArea.ISize(wm); + } + } else { + nscoord iStartOffset, iEndOffset; + ComputeFloatAvoidingOffsets(aFrame, aFloatAvailableSpace.mRect, + iStartOffset, iEndOffset); + result.IStart(wm) = mContentArea.IStart(wm) + iStartOffset; + result.ISize(wm) = mContentArea.ISize(wm) - iStartOffset - iEndOffset; + } + +#ifdef REALLY_NOISY_REFLOW + printf(" CBAS: result %d %d %d %d\n", result.IStart(wm), result.BStart(wm), + result.ISize(wm), result.BSize(wm)); +#endif + + return result; +} + +LogicalSize BlockReflowState::ComputeAvailableSizeForFloat() const { + const auto wm = mReflowInput.GetWritingMode(); + const nscoord availBSize = ContentBSize() == NS_UNCONSTRAINEDSIZE + ? NS_UNCONSTRAINEDSIZE + : std::max(0, ContentBEnd() - mBCoord); + return LogicalSize(wm, ContentISize(), availBSize); +} + +bool BlockReflowState::FloatAvoidingBlockFitsInAvailSpace( + nsIFrame* aFloatAvoidingBlock, + const nsFlowAreaRect& aFloatAvailableSpace) const { + if (!aFloatAvailableSpace.HasFloats()) { + // If there aren't any floats here, then we always fit. + // We check this before calling ISizeToClearPastFloats, which is + // somewhat expensive. + return true; + } + + // |aFloatAvailableSpace| was computed as having a negative size, which means + // there are floats on both sides pushing inwards past each other, and + // |aFloatAvoidingBlock| would necessarily intersect a float if we put it + // here. So, it doesn't fit. + if (aFloatAvailableSpace.ISizeIsActuallyNegative()) { + return false; + } + + WritingMode wm = mReflowInput.GetWritingMode(); + nsBlockFrame::FloatAvoidingISizeToClear replacedISize = + nsBlockFrame::ISizeToClearPastFloats(*this, aFloatAvailableSpace.mRect, + aFloatAvoidingBlock); + // The inline-start side of the replaced element should be offset by + // the larger of the float intrusion or the replaced element's own + // start margin. The inline-end side is similar, except for Web + // compatibility we ignore the margin. + return std::max( + aFloatAvailableSpace.mRect.IStart(wm) - mContentArea.IStart(wm), + replacedISize.marginIStart) + + replacedISize.borderBoxISize + + (mContentArea.IEnd(wm) - aFloatAvailableSpace.mRect.IEnd(wm)) <= + mContentArea.ISize(wm); +} + +nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceWithState( + nscoord aBCoord, ShapeType aShapeType, + nsFloatManager::SavedState* aState) const { + WritingMode wm = mReflowInput.GetWritingMode(); +#ifdef DEBUG + // Verify that the caller setup the coordinate system properly + nscoord wI, wB; + FloatManager()->GetTranslation(wI, wB); + + NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB), + "bad coord system"); +#endif + + nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX) + ? nscoord_MAX + : std::max(mContentArea.BEnd(wm) - aBCoord, 0); + nsFlowAreaRect result = FloatManager()->GetFlowArea( + wm, aBCoord, blockSize, BandInfoType::BandFromPoint, aShapeType, + mContentArea, aState, ContainerSize()); + // Keep the inline size >= 0 for compatibility with nsSpaceManager. + if (result.mRect.ISize(wm) < 0) { + result.mRect.ISize(wm) = 0; + } + +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("%s: band=%d,%d,%d,%d hasfloats=%d\n", __func__, + result.mRect.IStart(wm), result.mRect.BStart(wm), + result.mRect.ISize(wm), result.mRect.BSize(wm), result.HasFloats()); + } +#endif + return result; +} + +nsFlowAreaRect BlockReflowState::GetFloatAvailableSpaceForBSize( + nscoord aBCoord, nscoord aBSize, nsFloatManager::SavedState* aState) const { + WritingMode wm = mReflowInput.GetWritingMode(); +#ifdef DEBUG + // Verify that the caller setup the coordinate system properly + nscoord wI, wB; + FloatManager()->GetTranslation(wI, wB); + + NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB), + "bad coord system"); +#endif + nsFlowAreaRect result = FloatManager()->GetFlowArea( + wm, aBCoord, aBSize, BandInfoType::WidthWithinHeight, + ShapeType::ShapeOutside, mContentArea, aState, ContainerSize()); + // Keep the width >= 0 for compatibility with nsSpaceManager. + if (result.mRect.ISize(wm) < 0) { + result.mRect.ISize(wm) = 0; + } + +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("%s: space=%d,%d,%d,%d hasfloats=%d\n", __func__, + result.mRect.IStart(wm), result.mRect.BStart(wm), + result.mRect.ISize(wm), result.mRect.BSize(wm), result.HasFloats()); + } +#endif + return result; +} + +/* + * Reconstruct the vertical margin before the line |aLine| in order to + * do an incremental reflow that begins with |aLine| without reflowing + * the line before it. |aLine| may point to the fencepost at the end of + * the line list, and it is used this way since we (for now, anyway) + * always need to recover margins at the end of a block. + * + * The reconstruction involves walking backward through the line list to + * find any collapsed margins preceding the line that would have been in + * the reflow input's |mPrevBEndMargin| when we reflowed that line in + * a full reflow (under the rule in CSS2 that all adjacent vertical + * margins of blocks collapse). + */ +void BlockReflowState::ReconstructMarginBefore(nsLineList::iterator aLine) { + mPrevBEndMargin.Zero(); + nsBlockFrame* block = mBlock; + + nsLineList::iterator firstLine = block->LinesBegin(); + for (;;) { + --aLine; + if (aLine->IsBlock()) { + mPrevBEndMargin = aLine->GetCarriedOutBEndMargin(); + break; + } + if (!aLine->IsEmpty()) { + break; + } + if (aLine == firstLine) { + // If the top margin was carried out (and thus already applied), + // set it to zero. Either way, we're done. + if (!mFlags.mIsBStartMarginRoot) { + mPrevBEndMargin.Zero(); + } + break; + } + } +} + +void BlockReflowState::SetupPushedFloatList() { + MOZ_ASSERT(!mFlags.mIsFloatListInBlockPropertyTable == !mPushedFloats, + "flag mismatch"); + if (!mFlags.mIsFloatListInBlockPropertyTable) { + // If we're being re-Reflow'd without our next-in-flow having been + // reflowed, some pushed floats from our previous reflow might + // still be on our pushed floats list. However, that's + // actually fine, since they'll all end up being stolen and + // reordered into the correct order again. + // (nsBlockFrame::ReflowDirtyLines ensures that any lines with + // pushed floats are reflowed.) + mPushedFloats = mBlock->EnsurePushedFloats(); + mFlags.mIsFloatListInBlockPropertyTable = true; + } +} + +void BlockReflowState::AppendPushedFloatChain(nsIFrame* aFloatCont) { + SetupPushedFloatList(); + while (true) { + aFloatCont->AddStateBits(NS_FRAME_IS_PUSHED_FLOAT); + mPushedFloats->AppendFrame(mBlock, aFloatCont); + aFloatCont = aFloatCont->GetNextInFlow(); + if (!aFloatCont || aFloatCont->GetParent() != mBlock) { + break; + } + mBlock->StealFrame(aFloatCont); + } +} + +/** + * Restore information about floats into the float manager for an + * incremental reflow, and simultaneously push the floats by + * |aDeltaBCoord|, which is the amount |aLine| was pushed relative to its + * parent. The recovery of state is one of the things that makes + * incremental reflow O(N^2) and this state should really be kept + * around, attached to the frame tree. + */ +void BlockReflowState::RecoverFloats(nsLineList::iterator aLine, + nscoord aDeltaBCoord) { + WritingMode wm = mReflowInput.GetWritingMode(); + if (aLine->HasFloats()) { + // Place the floats into the float manager again. Also slide + // them, just like the regular frames on the line. + for (nsIFrame* floatFrame : aLine->Floats()) { + if (aDeltaBCoord != 0) { + floatFrame->MovePositionBy(nsPoint(0, aDeltaBCoord)); + nsContainerFrame::PositionFrameView(floatFrame); + nsContainerFrame::PositionChildViews(floatFrame); + } +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { + nscoord tI, tB; + FloatManager()->GetTranslation(tI, tB); + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI, tB, mFloatManagerI, + mFloatManagerB); + floatFrame->ListTag(stdout); + LogicalRect region = + nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()); + printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n", aDeltaBCoord, + region.IStart(wm), region.BStart(wm), region.ISize(wm), + region.BSize(wm)); + } +#endif + FloatManager()->AddFloat( + floatFrame, + nsFloatManager::GetRegionFor(wm, floatFrame, ContainerSize()), wm, + ContainerSize()); + } + } else if (aLine->IsBlock()) { + nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *FloatManager(), wm, + ContainerSize()); + } +} + +/** + * Everything done in this function is done O(N) times for each pass of + * reflow so it is O(N*M) where M is the number of incremental reflow + * passes. That's bad. Don't do stuff here. + * + * When this function is called, |aLine| has just been slid by |aDeltaBCoord| + * and the purpose of RecoverStateFrom is to ensure that the + * BlockReflowState is in the same state that it would have been in + * had the line just been reflowed. + * + * Most of the state recovery that we have to do involves floats. + */ +void BlockReflowState::RecoverStateFrom(nsLineList::iterator aLine, + nscoord aDeltaBCoord) { + // Make the line being recovered the current line + mCurrentLine = aLine; + + // Place floats for this line into the float manager + if (aLine->HasFloats() || aLine->IsBlock()) { + RecoverFloats(aLine, aDeltaBCoord); + +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { + FloatManager()->List(stdout); + } +#endif + } +} + +// This is called by the line layout's AddFloat method when a +// place-holder frame is reflowed in a line. If the float is a +// left-most child (it's x coordinate is at the line's left margin) +// then the float is place immediately, otherwise the float +// placement is deferred until the line has been reflowed. + +// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- +// technically we're supposed let the current line flow around the +// float as well unless it won't fit next to what we already have. +// But nobody else implements it that way... +bool BlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, + nscoord aAvailableISize) { + MOZ_ASSERT(aLineLayout, "must have line layout"); + MOZ_ASSERT(mBlock->LinesEnd() != mCurrentLine, "null ptr"); + MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), + "aFloat must be an out-of-flow frame"); + + MOZ_ASSERT(aFloat->GetParent(), "float must have parent"); + MOZ_ASSERT(aFloat->GetParent()->IsBlockFrameOrSubclass(), + "float's parent must be block"); + if (aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT) || + aFloat->GetParent() != mBlock) { + MOZ_ASSERT(aFloat->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT | + NS_FRAME_FIRST_REFLOW), + "float should be in this block unless it was marked as " + "pushed float, or just inserted"); + MOZ_ASSERT(aFloat->GetParent()->FirstContinuation() == + mBlock->FirstContinuation()); + // If, in a previous reflow, the float was pushed entirely to + // another column/page, we need to steal it back. (We might just + // push it again, though.) Likewise, if that previous reflow + // reflowed this block but not its next continuation, we might need + // to steal it from our own float-continuations list. + // + // For more about pushed floats, see the comment above + // nsBlockFrame::DrainPushedFloats. + auto* floatParent = static_cast(aFloat->GetParent()); + floatParent->StealFrame(aFloat); + + aFloat->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT); + + // Appending is fine, since if a float was pushed to the next + // page/column, all later floats were also pushed. + mBlock->mFloats.AppendFrame(mBlock, aFloat); + } + + // Because we are in the middle of reflowing a placeholder frame + // within a line (and possibly nested in an inline frame or two + // that's a child of our block) we need to restore the space + // manager's translation to the space that the block resides in + // before placing the float. + nscoord oI, oB; + FloatManager()->GetTranslation(oI, oB); + nscoord dI = oI - mFloatManagerI; + nscoord dB = oB - mFloatManagerB; + FloatManager()->Translate(-dI, -dB); + + bool placed = false; + + // Now place the float immediately if possible. Otherwise stash it + // away in mBelowCurrentLineFloats and place it later. + // If one or more floats has already been pushed to the next line, + // don't let this one go on the current line, since that would violate + // float ordering. + bool shouldPlaceFloatBelowCurrentLine = false; + if (mBelowCurrentLineFloats.IsEmpty()) { + // If the current line is empty, we don't impose any inline-size constraint + // from the line layout. + Maybe availableISizeInCurrentLine = + aLineLayout->LineIsEmpty() ? Nothing() : Some(aAvailableISize); + PlaceFloatResult result = + FlowAndPlaceFloat(aFloat, availableISizeInCurrentLine); + if (result == PlaceFloatResult::Placed) { + placed = true; + // Pass on updated available space to the current inline reflow engine + WritingMode wm = mReflowInput.GetWritingMode(); + // If we have mLineBSize, we are reflowing the line again due to + // LineReflowStatus::RedoMoreFloats. We should use mLineBSize to query the + // correct available space. + nsFlowAreaRect floatAvailSpace = + mLineBSize.isNothing() ? GetFloatAvailableSpace(mBCoord) + : GetFloatAvailableSpaceForBSize( + mBCoord, mLineBSize.value(), nullptr); + LogicalRect availSpace(wm, floatAvailSpace.mRect.IStart(wm), mBCoord, + floatAvailSpace.mRect.ISize(wm), + floatAvailSpace.mRect.BSize(wm)); + aLineLayout->UpdateBand(wm, availSpace, aFloat); + // Record this float in the current-line list + mCurrentLineFloats.AppendElement(aFloat); + } else if (result == PlaceFloatResult::ShouldPlaceInNextContinuation) { + (*aLineLayout->GetLine())->SetHadFloatPushed(); + } else { + MOZ_ASSERT(result == PlaceFloatResult::ShouldPlaceBelowCurrentLine); + shouldPlaceFloatBelowCurrentLine = true; + } + } else { + shouldPlaceFloatBelowCurrentLine = true; + } + + if (shouldPlaceFloatBelowCurrentLine) { + // Always claim to be placed; we don't know whether we fit yet, so we + // deal with this in PlaceBelowCurrentLineFloats + placed = true; + // This float will be placed after the line is done (it is a + // below-current-line float). + mBelowCurrentLineFloats.AppendElement(aFloat); + } + + // Restore coordinate system + FloatManager()->Translate(dI, dB); + + return placed; +} + +bool BlockReflowState::CanPlaceFloat( + nscoord aFloatISize, const nsFlowAreaRect& aFloatAvailableSpace) { + // A float fits at a given block-dir position if there are no floats + // at its inline-dir position (no matter what its inline size) or if + // its inline size fits in the space remaining after prior floats have + // been placed. + // FIXME: We should allow overflow by up to half a pixel here (bug 21193). + return !aFloatAvailableSpace.HasFloats() || + aFloatAvailableSpace.mRect.ISize(mReflowInput.GetWritingMode()) >= + aFloatISize; +} + +// Return the inline-size that the float (including margins) will take up +// in the writing mode of the containing block. If this returns +// NS_UNCONSTRAINEDSIZE, we're dealing with an orthogonal block that +// has block-size:auto, and we'll need to actually reflow it to find out +// how much inline-size it will occupy in the containing block's mode. +static nscoord FloatMarginISize(WritingMode aCBWM, + const ReflowInput& aFloatRI) { + if (aFloatRI.ComputedSize(aCBWM).ISize(aCBWM) == NS_UNCONSTRAINEDSIZE) { + return NS_UNCONSTRAINEDSIZE; // reflow is needed to get the true size + } + return aFloatRI.ComputedSizeWithMarginBorderPadding(aCBWM).ISize(aCBWM); +} + +// A frame property that stores the last shape source / margin / etc. if there's +// any shape, in order to invalidate the float area properly when it changes. +// +// TODO(emilio): This could really belong to GetRegionFor / StoreRegionFor, but +// when I tried it was a bit awkward because of the logical -> physical +// conversion that happens there. +// +// Maybe all this code could be refactored to make this cleaner, but keeping the +// two properties separated was slightly nicer. +struct ShapeInvalidationData { + StyleShapeOutside mShapeOutside{StyleShapeOutside::None()}; + float mShapeImageThreshold = 0.0; + LengthPercentage mShapeMargin; + + ShapeInvalidationData() = default; + + explicit ShapeInvalidationData(const nsStyleDisplay& aDisplay) { + Update(aDisplay); + } + + static bool IsNeeded(const nsStyleDisplay& aDisplay) { + return !aDisplay.mShapeOutside.IsNone(); + } + + void Update(const nsStyleDisplay& aDisplay) { + MOZ_ASSERT(IsNeeded(aDisplay)); + mShapeOutside = aDisplay.mShapeOutside; + mShapeImageThreshold = aDisplay.mShapeImageThreshold; + mShapeMargin = aDisplay.mShapeMargin; + } + + bool Matches(const nsStyleDisplay& aDisplay) const { + return mShapeOutside == aDisplay.mShapeOutside && + mShapeImageThreshold == aDisplay.mShapeImageThreshold && + mShapeMargin == aDisplay.mShapeMargin; + } +}; + +NS_DECLARE_FRAME_PROPERTY_DELETABLE(ShapeInvalidationDataProperty, + ShapeInvalidationData) + +BlockReflowState::PlaceFloatResult BlockReflowState::FlowAndPlaceFloat( + nsIFrame* aFloat, Maybe aAvailableISizeInCurrentLine) { + MOZ_ASSERT(aFloat->GetParent() == mBlock, "Float frame has wrong parent"); + + WritingMode wm = mReflowInput.GetWritingMode(); + // Save away the block-dir coordinate before placing the float. We will + // restore mBCoord at the end after placing the float. This is + // necessary because any adjustments to mBCoord during the float + // placement are for the float only, not for any non-floating + // content. + AutoRestore restoreBCoord(mBCoord); + + // Whether the block-direction position available to place a float has been + // pushed down due to the presence of other floats. + auto HasFloatPushedDown = [this, &restoreBCoord]() { + return mBCoord != restoreBCoord.SavedValue(); + }; + + // Grab the float's display information + const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay(); + + // The float's old region, so we can propagate damage. + LogicalRect oldRegion = + nsFloatManager::GetRegionFor(wm, aFloat, ContainerSize()); + + ShapeInvalidationData* invalidationData = + aFloat->GetProperty(ShapeInvalidationDataProperty()); + + // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't + // ``above'' another float that preceded it in the flow. + mBCoord = std::max(FloatManager()->LowestFloatBStart(), mBCoord); + + // See if the float should clear any preceding floats... + // XXX We need to mark this float somehow so that it gets reflowed + // when floats are inserted before it. + if (StyleClear::None != floatDisplay->mClear) { + // XXXldb Does this handle vertical margins correctly? + auto [bCoord, result] = ClearFloats(mBCoord, floatDisplay->mClear); + if (result == ClearFloatsResult::FloatsPushedOrSplit) { + PushFloatPastBreak(aFloat); + return PlaceFloatResult::ShouldPlaceInNextContinuation; + } + mBCoord = bCoord; + } + + LogicalSize availSize = ComputeAvailableSizeForFloat(); + const WritingMode floatWM = aFloat->GetWritingMode(); + Maybe floatRI(std::in_place, mPresContext, mReflowInput, aFloat, + availSize.ConvertTo(floatWM, wm)); + + nscoord floatMarginISize = FloatMarginISize(wm, *floatRI); + LogicalMargin floatMargin = floatRI->ComputedLogicalMargin(wm); + nsReflowStatus reflowStatus; + + // If it's a floating first-letter, we need to reflow it before we + // know how wide it is (since we don't compute which letters are part + // of the first letter until reflow!). + // We also need to do this early reflow if FloatMarginISize returned + // an unconstrained inline-size, which can occur if the float had an + // orthogonal writing mode and 'auto' block-size (in its mode). + bool earlyFloatReflow = + aFloat->IsLetterFrame() || floatMarginISize == NS_UNCONSTRAINEDSIZE; + if (earlyFloatReflow) { + mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus); + floatMarginISize = aFloat->ISize(wm) + floatMargin.IStartEnd(wm); + NS_ASSERTION(reflowStatus.IsComplete(), + "letter frames and orthogonal floats with auto block-size " + "shouldn't break, and if they do now, then they're breaking " + "at the wrong point"); + } + + // Now we've computed the float's margin inline-size. + if (aAvailableISizeInCurrentLine && + floatMarginISize > *aAvailableISizeInCurrentLine) { + // The float cannot fit in the available inline-size of the current line. + // Let's notify our caller to place it later. + return PlaceFloatResult::ShouldPlaceBelowCurrentLine; + } + + // Find a place to place the float. The CSS2 spec doesn't want + // floats overlapping each other or sticking out of the containing + // block if possible (CSS2 spec section 9.5.1, see the rule list). + StyleFloat floatStyle = floatDisplay->mFloat; + MOZ_ASSERT(StyleFloat::Left == floatStyle || StyleFloat::Right == floatStyle, + "Invalid float type!"); + + // Are we required to place at least part of the float because we're + // at the top of the page (to avoid an infinite loop of pushing and + // breaking). + bool mustPlaceFloat = + mReflowInput.mFlags.mIsTopOfPage && IsAdjacentWithBStart(); + + // Get the band of available space with respect to margin box. + nsFlowAreaRect floatAvailableSpace = + GetFloatAvailableSpaceForPlacingFloat(mBCoord); + + for (;;) { + if (mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE && + floatAvailableSpace.mRect.BSize(wm) <= 0 && !mustPlaceFloat) { + // No space, nowhere to put anything. + PushFloatPastBreak(aFloat); + return PlaceFloatResult::ShouldPlaceInNextContinuation; + } + + if (CanPlaceFloat(floatMarginISize, floatAvailableSpace)) { + // We found an appropriate place. + break; + } + + // Nope. try to advance to the next band. + mBCoord += floatAvailableSpace.mRect.BSize(wm); + floatAvailableSpace = GetFloatAvailableSpaceForPlacingFloat(mBCoord); + mustPlaceFloat = false; + } + + // If the float is continued, it will get the same absolute x value as its + // prev-in-flow + + // We don't worry about the geometry of the prev in flow, let the continuation + // place and size itself as required. + + // Assign inline and block dir coordinates to the float. We don't use + // LineLeft() and LineRight() here, because we would only have to + // convert the result back into this block's writing mode. + LogicalPoint floatPos(wm); + bool leftFloat = floatStyle == StyleFloat::Left; + + if (leftFloat == wm.IsBidiLTR()) { + floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm); + } else { + floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize; + } + // CSS2 spec, 9.5.1 rule [4]: "A floating box's outer top may not + // be higher than the top of its containing block." (Since the + // containing block is the content edge of the block box, this + // means the margin edge of the float can't be higher than the + // content edge of the block that contains it.) + floatPos.B(wm) = std::max(mBCoord, ContentBStart()); + + // Reflow the float after computing its vertical position so it knows + // where to break. + if (!earlyFloatReflow) { + const LogicalSize oldAvailSize = availSize; + availSize = ComputeAvailableSizeForFloat(); + if (oldAvailSize != availSize) { + floatRI.reset(); + floatRI.emplace(mPresContext, mReflowInput, aFloat, + availSize.ConvertTo(floatWM, wm)); + } + // Normally the mIsTopOfPage state is copied from the parent reflow input. + // However, when reflowing a float, if we've placed other floats that force + // this float being pushed down, we should unset the mIsTopOfPage bit. + if (floatRI->mFlags.mIsTopOfPage && HasFloatPushedDown()) { + // HasFloatPushedDown() implies that we increased mBCoord, and we + // should've turned off mustPlaceFloat when we did that. + NS_ASSERTION(!mustPlaceFloat, + "mustPlaceFloat shouldn't be set if we're not at the " + "top-of-page!"); + floatRI->mFlags.mIsTopOfPage = false; + } + mBlock->ReflowFloat(*this, *floatRI, aFloat, reflowStatus); + } + if (aFloat->GetPrevInFlow()) { + floatMargin.BStart(wm) = 0; + } + if (reflowStatus.IsIncomplete()) { + floatMargin.BEnd(wm) = 0; + } + + // If the float cannot fit (e.g. via fragmenting itself if applicable), or if + // we're forced to break before it for CSS break-* reasons, then it needs to + // be pushed in its entirety to the next column/page. + // + // Note we use the available block-size in floatRI rather than use + // availSize.BSize() because nsBlockReflowContext::ReflowBlock() might adjust + // floatRI's available size. + const nscoord availBSize = floatRI->AvailableSize(floatWM).BSize(floatWM); + const bool isTruncated = + availBSize != NS_UNCONSTRAINEDSIZE && aFloat->BSize(floatWM) > availBSize; + if ((!floatRI->mFlags.mIsTopOfPage && isTruncated) || + reflowStatus.IsInlineBreakBefore()) { + PushFloatPastBreak(aFloat); + return PlaceFloatResult::ShouldPlaceInNextContinuation; + } + + // We can't use aFloat->ShouldAvoidBreakInside(mReflowInput) here since + // its mIsTopOfPage may be true even though the float isn't at the + // top when floatPos.B(wm) > 0. + if (ContentBSize() != NS_UNCONSTRAINEDSIZE && !mustPlaceFloat && + (!mReflowInput.mFlags.mIsTopOfPage || floatPos.B(wm) > 0) && + StyleBreakWithin::Avoid == aFloat->StyleDisplay()->mBreakInside && + (!reflowStatus.IsFullyComplete() || + aFloat->BSize(wm) + floatMargin.BStartEnd(wm) > + ContentBEnd() - floatPos.B(wm)) && + !aFloat->GetPrevInFlow()) { + PushFloatPastBreak(aFloat); + return PlaceFloatResult::ShouldPlaceInNextContinuation; + } + + // Calculate the actual origin of the float frame's border rect + // relative to the parent block; the margin must be added in + // to get the border rect + LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm), + floatMargin.BStart(wm) + floatPos.B(wm)); + + // If float is relatively positioned, factor that in as well + const LogicalMargin floatOffsets = floatRI->ComputedLogicalOffsets(wm); + ReflowInput::ApplyRelativePositioning(aFloat, wm, floatOffsets, &origin, + ContainerSize()); + + // Position the float and make sure and views are properly + // positioned. We need to explicitly position its child views as + // well, since we're moving the float after flowing it. + bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin; + if (moved) { + aFloat->SetPosition(wm, origin, ContainerSize()); + nsContainerFrame::PositionFrameView(aFloat); + nsContainerFrame::PositionChildViews(aFloat); + } + + // Update the float combined area state + // XXX Floats should really just get invalidated here if necessary + mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreasRelativeToParent()); + + // Place the float in the float manager + // calculate region + LogicalRect region = nsFloatManager::CalculateRegionFor( + wm, aFloat, floatMargin, ContainerSize()); + // if the float split, then take up all of the vertical height + if (reflowStatus.IsIncomplete() && (NS_UNCONSTRAINEDSIZE != ContentBSize())) { + region.BSize(wm) = + std::max(region.BSize(wm), ContentBSize() - floatPos.B(wm)); + } + FloatManager()->AddFloat(aFloat, region, wm, ContainerSize()); + + // store region + nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize()); + + const bool invalidationDataNeeded = + ShapeInvalidationData::IsNeeded(*floatDisplay); + + // If the float's dimensions or shape have changed, note the damage in the + // float manager. + if (!region.IsEqualEdges(oldRegion) || + !!invalidationData != invalidationDataNeeded || + (invalidationData && !invalidationData->Matches(*floatDisplay))) { + // XXXwaterson conservative: we could probably get away with noting + // less damage; e.g., if only height has changed, then only note the + // area into which the float has grown or from which the float has + // shrunk. + nscoord blockStart = std::min(region.BStart(wm), oldRegion.BStart(wm)); + nscoord blockEnd = std::max(region.BEnd(wm), oldRegion.BEnd(wm)); + FloatManager()->IncludeInDamage(blockStart, blockEnd); + } + + if (invalidationDataNeeded) { + if (invalidationData) { + invalidationData->Update(*floatDisplay); + } else { + aFloat->SetProperty(ShapeInvalidationDataProperty(), + new ShapeInvalidationData(*floatDisplay)); + } + } else if (invalidationData) { + invalidationData = nullptr; + aFloat->RemoveProperty(ShapeInvalidationDataProperty()); + } + + if (!reflowStatus.IsFullyComplete()) { + mBlock->SplitFloat(*this, aFloat, reflowStatus); + } else { + MOZ_ASSERT(!aFloat->GetNextInFlow()); + } + +#ifdef DEBUG + if (nsBlockFrame::gNoisyFloatManager) { + nscoord tI, tB; + FloatManager()->GetTranslation(tI, tB); + mBlock->ListTag(stdout); + printf(": FlowAndPlaceFloat: AddFloat: tIB=%d,%d (%d,%d) {%d,%d,%d,%d}\n", + tI, tB, mFloatManagerI, mFloatManagerB, region.IStart(wm), + region.BStart(wm), region.ISize(wm), region.BSize(wm)); + } + + if (nsBlockFrame::gNoisyReflow) { + nsRect r = aFloat->GetRect(); + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("placed float: "); + aFloat->ListTag(stdout); + printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height); + } +#endif + + return PlaceFloatResult::Placed; +} + +void BlockReflowState::PushFloatPastBreak(nsIFrame* aFloat) { + // This ensures that we: + // * don't try to place later but smaller floats (which CSS says + // must have their tops below the top of this float) + // * don't waste much time trying to reflow this float again until + // after the break + StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat; + if (floatStyle == StyleFloat::Left) { + FloatManager()->SetPushedLeftFloatPastBreak(); + } else { + MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float value!"); + FloatManager()->SetPushedRightFloatPastBreak(); + } + + // Put the float on the pushed floats list, even though it + // isn't actually a continuation. + mBlock->StealFrame(aFloat); + AppendPushedFloatChain(aFloat); + mReflowStatus.SetOverflowIncomplete(); +} + +/** + * Place below-current-line floats. + */ +void BlockReflowState::PlaceBelowCurrentLineFloats(nsLineBox* aLine) { + MOZ_ASSERT(!mBelowCurrentLineFloats.IsEmpty()); + nsTArray floatsPlacedInLine; + for (nsIFrame* f : mBelowCurrentLineFloats) { +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("placing bcl float: "); + f->ListTag(stdout); + printf("\n"); + } +#endif + // Place the float + PlaceFloatResult result = FlowAndPlaceFloat(f); + MOZ_ASSERT(result != PlaceFloatResult::ShouldPlaceBelowCurrentLine, + "We are already dealing with below current line floats!"); + if (result == PlaceFloatResult::Placed) { + floatsPlacedInLine.AppendElement(f); + } + } + if (floatsPlacedInLine.Length() != mBelowCurrentLineFloats.Length()) { + // We have some floats having ShouldPlaceInNextContinuation result. + aLine->SetHadFloatPushed(); + } + aLine->AppendFloats(std::move(floatsPlacedInLine)); + mBelowCurrentLineFloats.Clear(); +} + +std::tuple +BlockReflowState::ClearFloats(nscoord aBCoord, StyleClear aClearType, + nsIFrame* aFloatAvoidingBlock) { +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("clear floats: in: aBCoord=%d\n", aBCoord); + } +#endif + + if (!FloatManager()->HasAnyFloats()) { + return {aBCoord, ClearFloatsResult::BCoordNoChange}; + } + + nscoord newBCoord = aBCoord; + + if (aClearType != StyleClear::None) { + newBCoord = FloatManager()->ClearFloats(newBCoord, aClearType); + + if (FloatManager()->ClearContinues(aClearType)) { + return {newBCoord, ClearFloatsResult::FloatsPushedOrSplit}; + } + } + + if (aFloatAvoidingBlock) { + for (;;) { + nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newBCoord); + if (FloatAvoidingBlockFitsInAvailSpace(aFloatAvoidingBlock, + floatAvailableSpace)) { + break; + } + // See the analogous code for inlines in + // nsBlockFrame::DoReflowInlineFrames + if (!AdvanceToNextBand(floatAvailableSpace.mRect, &newBCoord)) { + // Stop trying to clear here; we'll just get pushed to the + // next column or page and try again there. + break; + } + } + } + +#ifdef DEBUG + if (nsBlockFrame::gNoisyReflow) { + nsIFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); + printf("clear floats: out: y=%d\n", newBCoord); + } +#endif + + ClearFloatsResult result = newBCoord == aBCoord + ? ClearFloatsResult::BCoordNoChange + : ClearFloatsResult::BCoordAdvanced; + return {newBCoord, result}; +} diff --git a/layout/generic/BlockReflowState.h b/layout/generic/BlockReflowState.h new file mode 100644 index 0000000000..9f219cc3f9 --- /dev/null +++ b/layout/generic/BlockReflowState.h @@ -0,0 +1,417 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* state used in reflow of block frames */ + +#ifndef BlockReflowState_h +#define BlockReflowState_h + +#include + +#include "mozilla/ReflowInput.h" +#include "nsFloatManager.h" +#include "nsLineBox.h" + +class nsBlockFrame; +class nsFrameList; +class nsOverflowContinuationTracker; + +namespace mozilla { + +// BlockReflowState contains additional reflow input information that the +// block frame uses along with ReflowInput. Like ReflowInput, this +// is read-only data that is passed down from a parent frame to its children. +class BlockReflowState { + using BandInfoType = nsFloatManager::BandInfoType; + using ShapeType = nsFloatManager::ShapeType; + + // Block reflow input flags. + struct Flags { + Flags() + : mIsBStartMarginRoot(false), + mIsBEndMarginRoot(false), + mShouldApplyBStartMargin(false), + mHasLineAdjacentToTop(false), + mBlockNeedsFloatManager(false), + mIsLineLayoutEmpty(false), + mIsFloatListInBlockPropertyTable(false), + mCanHaveOverflowMarkers(false) {} + + // Set in the BlockReflowState constructor when reflowing a "block margin + // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for + // which margins apply by default). + // + // The flag is also set when reflowing a frame whose computed BStart border + // padding is non-zero. + bool mIsBStartMarginRoot : 1; + + // Set in the BlockReflowState constructor when reflowing a "block margin + // root" frame (i.e. a frame with the NS_BLOCK_MARGIN_ROOT flag set, for + // which margins apply by default). + // + // The flag is also set when reflowing a frame whose computed BEnd border + // padding is non-zero. + bool mIsBEndMarginRoot : 1; + + // Set if the BStart margin should be considered when placing a linebox that + // contains a block frame. It may be set as a side-effect of calling + // nsBlockFrame::ShouldApplyBStartMargin(); once set, + // ShouldApplyBStartMargin() uses it as a fast-path way to return whether + // the BStart margin should apply. + // + // If the flag hasn't been set in the block reflow state, then + // ShouldApplyBStartMargin() will crawl the line list to see if a block + // frame precedes the specified frame. If so, the BStart margin should be + // applied, and the flag is set to cache the result. (If not, the BStart + // margin will be applied as a result of the generational margin collapsing + // logic in nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this + // case, the flag won't be set, so subsequent calls to + // ShouldApplyBStartMargin() will continue crawl the line list.) + // + // This flag is also set in the BlockReflowState constructor if + // mIsBStartMarginRoot is set; that is, the frame being reflowed is a margin + // root by default. + bool mShouldApplyBStartMargin : 1; + + // Set when mLineAdjacentToTop is valid. + bool mHasLineAdjacentToTop : 1; + + // Set when the block has the equivalent of NS_BLOCK_FLOAT_MGR. + bool mBlockNeedsFloatManager : 1; + + // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing + // the current line. + bool mIsLineLayoutEmpty : 1; + + // Set when our mPushedFloats list is stored on the block's property table. + bool mIsFloatListInBlockPropertyTable : 1; + + // Set when we need text-overflow or -webkit-line-clamp processing. + bool mCanHaveOverflowMarkers : 1; + }; + + public: + BlockReflowState(const ReflowInput& aReflowInput, nsPresContext* aPresContext, + nsBlockFrame* aFrame, bool aBStartMarginRoot, + bool aBEndMarginRoot, bool aBlockNeedsFloatManager, + const nscoord aConsumedBSize, + const nscoord aEffectiveContentBoxBSize); + + /** + * Get the available reflow space (the area not occupied by floats) + * for the current y coordinate. The available space is relative to + * our coordinate system, which is the content box, with (0, 0) in the + * upper left. + * + * Returns whether there are floats present at the given block-direction + * coordinate and within the inline size of the content rect. + * + * Note: some codepaths clamp this structure's inline-size to be >=0 "for + * compatibility with nsSpaceManager". So if you encounter a nsFlowAreaRect + * which appears to have an ISize of 0, you can't necessarily assume that a + * 0-ISize float-avoiding block would actually fit; you need to check the + * InitialISizeIsNegative flag to see whether that 0 is actually a clamped + * negative value (in which case a 0-ISize float-avoiding block *should not* + * be considered as fitting, because it would intersect some float). + */ + nsFlowAreaRect GetFloatAvailableSpace() const { + return GetFloatAvailableSpace(mBCoord); + } + nsFlowAreaRect GetFloatAvailableSpaceForPlacingFloat(nscoord aBCoord) const { + return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::Margin, nullptr); + } + nsFlowAreaRect GetFloatAvailableSpace(nscoord aBCoord) const { + return GetFloatAvailableSpaceWithState(aBCoord, ShapeType::ShapeOutside, + nullptr); + } + nsFlowAreaRect GetFloatAvailableSpaceWithState( + nscoord aBCoord, ShapeType aShapeType, + nsFloatManager::SavedState* aState) const; + nsFlowAreaRect GetFloatAvailableSpaceForBSize( + nscoord aBCoord, nscoord aBSize, + nsFloatManager::SavedState* aState) const; + + // @return true if AddFloat was able to place the float; false if the float + // did not fit in available space. + // + // Note: if it returns false, then the float's position and size should be + // considered stale/invalid (until the float is successfully placed). + bool AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, + nscoord aAvailableISize); + + enum class PlaceFloatResult : uint8_t { + Placed, + ShouldPlaceBelowCurrentLine, + ShouldPlaceInNextContinuation, + }; + // @param aAvailableISizeInCurrentLine the available inline-size of the + // current line if current line is not empty. + PlaceFloatResult FlowAndPlaceFloat( + nsIFrame* aFloat, mozilla::Maybe aAvailableISizeInCurrentLine = + mozilla::Nothing()); + + void PlaceBelowCurrentLineFloats(nsLineBox* aLine); + + // Returns the first coordinate >= aBCoord that clears the + // floats indicated by aClearType and has enough inline size between floats + // (or no floats remaining) to accomodate aFloatAvoidingBlock. + enum class ClearFloatsResult : uint8_t { + BCoordNoChange, + BCoordAdvanced, + FloatsPushedOrSplit, + }; + std::tuple ClearFloats( + nscoord aBCoord, StyleClear aClearType, + nsIFrame* aFloatAvoidingBlock = nullptr); + + nsFloatManager* FloatManager() const { + MOZ_ASSERT(mReflowInput.mFloatManager, + "Float manager should be valid during the lifetime of " + "BlockReflowState!"); + return mReflowInput.mFloatManager; + } + + // Advances to the next band, i.e., the next horizontal stripe in + // which there is a different set of floats. + // Return false if it did not advance, which only happens for + // constrained heights (and means that we should get pushed to the + // next column/page). + bool AdvanceToNextBand(const LogicalRect& aFloatAvailableSpace, + nscoord* aBCoord) const { + WritingMode wm = mReflowInput.GetWritingMode(); + if (aFloatAvailableSpace.BSize(wm) > 0) { + // See if there's room in the next band. + *aBCoord += aFloatAvailableSpace.BSize(wm); + } else { + if (mReflowInput.AvailableHeight() != NS_UNCONSTRAINEDSIZE) { + // Stop trying to clear here; we'll just get pushed to the + // next column or page and try again there. + return false; + } + MOZ_ASSERT_UNREACHABLE("avail space rect with zero height!"); + *aBCoord += 1; + } + return true; + } + + bool FloatAvoidingBlockFitsInAvailSpace( + nsIFrame* aFloatAvoidingBlock, + const nsFlowAreaRect& aFloatAvailableSpace) const; + + // True if the current block-direction coordinate, for placing the children + // within the content area, is still adjacent with the block-start of the + // content area. + bool IsAdjacentWithBStart() const { return mBCoord == ContentBStart(); } + + const LogicalMargin& BorderPadding() const { return mBorderPadding; } + + // Reconstruct the previous block-end margin that goes before |aLine|. + void ReconstructMarginBefore(nsLineList::iterator aLine); + + // Caller must have called GetFloatAvailableSpace for the correct position + // (which need not be the current mBCoord). + void ComputeFloatAvoidingOffsets(nsIFrame* aFloatAvoidingBlock, + const LogicalRect& aFloatAvailableSpace, + nscoord& aIStartResult, + nscoord& aIEndResult) const; + + // Compute the amount of available space for reflowing a block frame at the + // current block-direction coordinate mBCoord. Caller must have called + // GetFloatAvailableSpace for the current mBCoord. + LogicalRect ComputeBlockAvailSpace(nsIFrame* aFrame, + const nsFlowAreaRect& aFloatAvailableSpace, + bool aBlockAvoidsFloats); + + LogicalSize ComputeAvailableSizeForFloat() const; + + void RecoverStateFrom(nsLineList::iterator aLine, nscoord aDeltaBCoord); + + void AdvanceToNextLine() { + if (mFlags.mIsLineLayoutEmpty) { + mFlags.mIsLineLayoutEmpty = false; + } else { + mLineNumber++; + } + } + + //---------------------------------------- + + // This state is the "global" state computed once for the reflow of + // the block. + + // The block frame that is using this object + nsBlockFrame* mBlock; + + nsPresContext* mPresContext; + + const ReflowInput& mReflowInput; + + // The coordinates within the float manager where the block is being + // placed after taking into account the blocks border and + // padding. This, therefore, represents the inner "content area" (in + // float manager coordinates) where child frames will be placed, + // including child blocks and floats. + nscoord mFloatManagerI, mFloatManagerB; + + // XXX get rid of this + nsReflowStatus mReflowStatus; + + // The float manager state as it was before the contents of this + // block. This is needed for positioning bullets, since we only want + // to move the bullet to flow around floats that were before this + // block, not floats inside of it. + nsFloatManager::SavedState mFloatManagerStateBefore; + + // The content area to reflow child frames within. This is within + // this frame's coordinate system and writing mode, which means + // mContentArea.IStart == BorderPadding().IStart and + // mContentArea.BStart == BorderPadding().BStart. + // The block size may be NS_UNCONSTRAINEDSIZE, which indicates that there + // is no page/column boundary below (the common case). + // mContentArea.BEnd() should only be called after checking that + // mContentArea.BSize is not NS_UNCONSTRAINEDSIZE; otherwise + // coordinate overflow may occur. + LogicalRect mContentArea; + nscoord ContentIStart() const { + return mContentArea.IStart(mReflowInput.GetWritingMode()); + } + nscoord ContentISize() const { + return mContentArea.ISize(mReflowInput.GetWritingMode()); + } + nscoord ContentIEnd() const { + return mContentArea.IEnd(mReflowInput.GetWritingMode()); + } + nscoord ContentBStart() const { + return mContentArea.BStart(mReflowInput.GetWritingMode()); + } + nscoord ContentBSize() const { + return mContentArea.BSize(mReflowInput.GetWritingMode()); + } + nscoord ContentBEnd() const { + NS_ASSERTION( + ContentBSize() != NS_UNCONSTRAINEDSIZE, + "ContentBSize() is unconstrained, so ContentBEnd() may overflow."); + return mContentArea.BEnd(mReflowInput.GetWritingMode()); + } + LogicalSize ContentSize(WritingMode aWM) const { + WritingMode wm = mReflowInput.GetWritingMode(); + return mContentArea.Size(wm).ConvertTo(aWM, wm); + } + + // Physical size. Use only for physical <-> logical coordinate conversion. + nsSize mContainerSize; + const nsSize& ContainerSize() const { return mContainerSize; } + + // Continuation out-of-flow float frames that need to move to our + // next in flow are placed here during reflow. It's a pointer to + // a frame list stored in the block's property table. + nsFrameList* mPushedFloats; + // This method makes sure pushed floats are accessible to + // StealFrame. Call it before adding any frames to mPushedFloats. + void SetupPushedFloatList(); + /** + * Append aFloatCont and its next-in-flows within the same block to + * mPushedFloats. aFloatCont should not be on any child list when + * making this call. Its next-in-flows will be removed from + * mBlock using StealFrame() before being added to mPushedFloats. + * All appended frames will be marked NS_FRAME_IS_PUSHED_FLOAT. + */ + void AppendPushedFloatChain(nsIFrame* aFloatCont); + + // Track child overflow continuations. + nsOverflowContinuationTracker* mOverflowTracker; + + //---------------------------------------- + + // This state is "running" state updated by the reflow of each line + // in the block. This same state is "recovered" when a line is not + // dirty and is passed over during incremental reflow. + + // The current line being reflowed + // If it is mBlock->end_lines(), then it is invalid. + nsLineList::iterator mCurrentLine; + + // When mHasLineAdjacentToTop is set, this refers to a line + // which we know is adjacent to the top of the block (in other words, + // all lines before it are empty and do not have clearance. This line is + // always before the current line. + nsLineList::iterator mLineAdjacentToTop; + + // The current block-direction coordinate in the block + nscoord mBCoord; + + // mBlock's computed logical border+padding with pre-reflow skip sides applied + // (See the constructor and nsIFrame::PreReflowBlockLevelLogicalSkipSides). + LogicalMargin mBorderPadding; + + // The overflow areas of all floats placed so far + OverflowAreas mFloatOverflowAreas; + + // Previous child. This is used when pulling up a frame to update + // the sibling list. + nsIFrame* mPrevChild; + + // The previous child frames collapsed bottom margin value. + nsCollapsingMargin mPrevBEndMargin; + + // The current next-in-flow for the block. When lines are pulled + // from a next-in-flow, this is used to know which next-in-flow to + // pull from. When a next-in-flow is emptied of lines, we advance + // this to the next next-in-flow. + nsBlockFrame* mNextInFlow; + + //---------------------------------------- + + // Temporary state, for line-reflow. This state is used during the reflow + // of a given line, but doesn't have meaning before or after. + + // The list of floats that are "current-line" floats. These are + // added to the line after the line has been reflowed, to keep the + // list fiddling from being N^2. + nsTArray mCurrentLineFloats; + + // The list of floats which are "below current-line" + // floats. These are reflowed/placed after the line is reflowed + // and placed. Again, this is done to keep the list fiddling from + // being N^2. + nsTArray mBelowCurrentLineFloats; + + // The list of floats that are waiting on a break opportunity in order to be + // placed, since we're on a nowrap context. + nsTArray mNoWrapFloats; + + nscoord mMinLineHeight; + + int32_t mLineNumber; + + Flags mFlags; + + // Cache the result of nsBlockFrame::FindTrailingClear() from mBlock's + // prev-in-flows. See nsBlockFrame::ReflowPushedFloats(). + StyleClear mTrailingClearFromPIF; + + // The amount of computed content block-size "consumed" by our previous + // continuations. + const nscoord mConsumedBSize; + + // Cache the current line's BSize if nsBlockFrame::PlaceLine() fails to + // place the line. When redoing the line, it will be used to query the + // accurate float available space in AddFloat() and + // nsBlockFrame::PlaceLine(). + Maybe mLineBSize; + + private: + bool CanPlaceFloat(nscoord aFloatISize, + const nsFlowAreaRect& aFloatAvailableSpace); + + void PushFloatPastBreak(nsIFrame* aFloat); + + void RecoverFloats(nsLineList::iterator aLine, nscoord aDeltaBCoord); +}; + +}; // namespace mozilla + +#endif // BlockReflowState_h diff --git a/layout/generic/CSSAlignUtils.cpp b/layout/generic/CSSAlignUtils.cpp new file mode 100644 index 0000000000..b88ec8bfa4 --- /dev/null +++ b/layout/generic/CSSAlignUtils.cpp @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Utility code for performing CSS Box Alignment */ + +#include "CSSAlignUtils.h" +#include "ReflowInput.h" + +namespace mozilla { + +static nscoord SpaceToFill(WritingMode aWM, const LogicalSize& aSize, + nscoord aMargin, LogicalAxis aAxis, + nscoord aCBSize) { + nscoord size = aSize.Size(aAxis, aWM); + return aCBSize - (size + aMargin); +} + +nscoord CSSAlignUtils::AlignJustifySelf(const StyleAlignFlags& aAlignment, + LogicalAxis aAxis, + AlignJustifyFlags aFlags, + nscoord aBaselineAdjust, + nscoord aCBSize, const ReflowInput& aRI, + const LogicalSize& aChildSize) { + MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO, + "auto values should have resolved already"); + MOZ_ASSERT(aAlignment != StyleAlignFlags::LEFT && + aAlignment != StyleAlignFlags::RIGHT, + "caller should map that to the corresponding START/END"); + + // Promote aFlags to convenience bools: + const bool isOverflowSafe = !!(aFlags & AlignJustifyFlags::OverflowSafe); + const bool isSameSide = !!(aFlags & AlignJustifyFlags::SameSide); + + StyleAlignFlags alignment = aAlignment; + // Map some alignment values to 'start' / 'end'. + if (alignment == StyleAlignFlags::SELF_START) { + // align/justify-self: self-start + alignment = + MOZ_LIKELY(isSameSide) ? StyleAlignFlags::START : StyleAlignFlags::END; + } else if (alignment == StyleAlignFlags::SELF_END) { + alignment = + MOZ_LIKELY(isSameSide) ? StyleAlignFlags::END : StyleAlignFlags::START; + // flex-start/flex-end are the same as start/end, in most contexts. + // (They have special behavior in flex containers, so flex containers + // should map them to some other value before calling this method.) + } else if (alignment == StyleAlignFlags::FLEX_START) { + alignment = StyleAlignFlags::START; + } else if (alignment == StyleAlignFlags::FLEX_END) { + alignment = StyleAlignFlags::END; + } + + // Get the item's margin corresponding to the container's start/end side. + WritingMode wm = aRI.GetWritingMode(); + const LogicalMargin margin = aRI.ComputedLogicalMargin(wm); + const auto startSide = MakeLogicalSide( + aAxis, MOZ_LIKELY(isSameSide) ? eLogicalEdgeStart : eLogicalEdgeEnd); + const nscoord marginStart = margin.Side(startSide, wm); + const auto endSide = GetOppositeSide(startSide); + const nscoord marginEnd = margin.Side(endSide, wm); + + const auto& styleMargin = aRI.mStyleMargin->mMargin; + bool hasAutoMarginStart; + bool hasAutoMarginEnd; + if (aFlags & AlignJustifyFlags::IgnoreAutoMargins) { + // (Note: ReflowInput will have treated "auto" margins as 0, so we + // don't need to do anything special to avoid expanding them.) + hasAutoMarginStart = hasAutoMarginEnd = false; + } else if (aAxis == eLogicalAxisBlock) { + hasAutoMarginStart = styleMargin.GetBStart(wm).IsAuto(); + hasAutoMarginEnd = styleMargin.GetBEnd(wm).IsAuto(); + } else { /* aAxis == eLogicalAxisInline */ + hasAutoMarginStart = styleMargin.GetIStart(wm).IsAuto(); + hasAutoMarginEnd = styleMargin.GetIEnd(wm).IsAuto(); + } + + // https://drafts.csswg.org/css-align-3/#overflow-values + // This implements = 'safe'. + // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins + if ((MOZ_UNLIKELY(isOverflowSafe) && alignment != StyleAlignFlags::START) || + hasAutoMarginStart || hasAutoMarginEnd) { + nscoord space = + SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize); + // XXX we might want to include == 0 here as an optimization - + // I need to see what the baseline/last baseline code looks like first. + if (space < 0) { + // "Overflowing elements ignore their auto margins and overflow + // in the end directions" + alignment = StyleAlignFlags::START; + } else if (hasAutoMarginEnd) { + alignment = hasAutoMarginStart ? StyleAlignFlags::CENTER + : (isSameSide ? StyleAlignFlags::START + : StyleAlignFlags::END); + } else if (hasAutoMarginStart) { + alignment = isSameSide ? StyleAlignFlags::END : StyleAlignFlags::START; + } + } + + // Determine the offset for the child frame (its border-box) which will + // achieve the requested alignment. + nscoord offset = 0; + if (alignment == StyleAlignFlags::BASELINE || + alignment == StyleAlignFlags::LAST_BASELINE) { + if (MOZ_LIKELY(isSameSide == (alignment == StyleAlignFlags::BASELINE))) { + offset = marginStart + aBaselineAdjust; + } else { + nscoord size = aChildSize.Size(aAxis, wm); + offset = aCBSize - (size + marginEnd) - aBaselineAdjust; + } + } else if (alignment == StyleAlignFlags::STRETCH || + alignment == StyleAlignFlags::START) { + // ComputeSize() deals with stretch + offset = marginStart; + } else if (alignment == StyleAlignFlags::END) { + nscoord size = aChildSize.Size(aAxis, wm); + offset = aCBSize - (size + marginEnd); + } else if (alignment == StyleAlignFlags::CENTER) { + nscoord size = aChildSize.Size(aAxis, wm); + offset = (aCBSize - size + marginStart - marginEnd) / 2; + } else { + MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value"); + } + + return offset; +} + +} // namespace mozilla diff --git a/layout/generic/CSSAlignUtils.h b/layout/generic/CSSAlignUtils.h new file mode 100644 index 0000000000..ae715f4081 --- /dev/null +++ b/layout/generic/CSSAlignUtils.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Utility code for performing CSS Box Alignment */ + +#ifndef mozilla_CSSAlignUtils_h +#define mozilla_CSSAlignUtils_h + +#include "mozilla/WritingModes.h" + +namespace mozilla { + +struct ReflowInput; +struct StyleAlignFlags; + +class CSSAlignUtils { + public: + /** + * Flags to customize the behavior of AlignJustifySelf: + */ + enum class AlignJustifyFlags { + NoFlags = 0, + // Indicates that we have = safe. + OverflowSafe = 1 << 0, + // Indicates that the container's start side in aAxis is the same + // as the child's start side in the child's parallel axis. + SameSide = 1 << 1, + // Indicates that AlignJustifySelf() shouldn't expand "auto" margins. + // (By default, AlignJustifySelf() *will* expand such margins, to fill the + // available space before any alignment is done.) + IgnoreAutoMargins = 1 << 2, + }; + + /** + * This computes the aligned offset of a CSS-aligned child within its + * alignment container. The returned offset is distance between the + * logical "start" edge of the alignment container & the logical "start" edge + * of the aligned child (in terms of the alignment container's writing mode). + * + * @param aAlignment An enumerated value representing a keyword for + * "align-self" or "justify-self". The values + * StyleAlignFlags::{AUTO,LEFT,RIGHT} must *not* be + * passed here; this method expects the caller to have + * already resolved those to 'start', 'end', or 'stretch'. + * @param aAxis The container's axis in which we're doing alignment. + * @param aBaselineAdjust The amount to offset baseline-aligned children. + * @param aCBSize The size of the alignment container, in its aAxis. + * @param aRI A ReflowInput for the child. + * @param aChildSize The child's LogicalSize (in its own writing mode). + */ + static nscoord AlignJustifySelf(const StyleAlignFlags& aAlignment, + LogicalAxis aAxis, AlignJustifyFlags aFlags, + nscoord aBaselineAdjust, nscoord aCBSize, + const ReflowInput& aRI, + const LogicalSize& aChildSize); +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSAlignUtils::AlignJustifyFlags) + +} // namespace mozilla + +#endif // mozilla_CSSAlignUtils_h diff --git a/layout/generic/CSSOrderAwareFrameIterator.cpp b/layout/generic/CSSOrderAwareFrameIterator.cpp new file mode 100644 index 0000000000..0c9520f378 --- /dev/null +++ b/layout/generic/CSSOrderAwareFrameIterator.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Iterator class for frame lists that respect CSS "order" during layout */ + +#include "CSSOrderAwareFrameIterator.h" +#include "nsIFrameInlines.h" + +static bool CanUse(const nsIFrame* aFrame) { + return aFrame->IsFlexOrGridContainer() || + (aFrame->GetContent() && aFrame->GetContent()->IsAnyOfXULElements( + nsGkAtoms::treecols, nsGkAtoms::treecol)); +} + +namespace mozilla { + +template <> +bool CSSOrderAwareFrameIterator::CanUse(const nsIFrame* aFrame) { + return ::CanUse(aFrame); +} + +template <> +bool ReverseCSSOrderAwareFrameIterator::CanUse(const nsIFrame* aFrame) { + return ::CanUse(aFrame); +} + +template <> +int CSSOrderAwareFrameIterator::CSSOrderComparator(nsIFrame* const& a, + nsIFrame* const& b) { + return a->StylePosition()->mOrder - b->StylePosition()->mOrder; +} + +template <> +int CSSOrderAwareFrameIterator::CSSBoxOrdinalGroupComparator( + nsIFrame* const& a, nsIFrame* const& b) { + return a->StyleXUL()->mBoxOrdinal - b->StyleXUL()->mBoxOrdinal; +} + +template <> +bool CSSOrderAwareFrameIterator::IsForward() const { + return true; +} + +template <> +nsFrameList::iterator CSSOrderAwareFrameIterator::begin( + const nsFrameList& aList) { + return aList.begin(); +} + +template <> +nsFrameList::iterator CSSOrderAwareFrameIterator::end( + const nsFrameList& aList) { + return aList.end(); +} + +template <> +int ReverseCSSOrderAwareFrameIterator::CSSOrderComparator(nsIFrame* const& a, + nsIFrame* const& b) { + return b->StylePosition()->mOrder - a->StylePosition()->mOrder; +} + +template <> +int ReverseCSSOrderAwareFrameIterator::CSSBoxOrdinalGroupComparator( + nsIFrame* const& a, nsIFrame* const& b) { + return b->StyleXUL()->mBoxOrdinal - a->StyleXUL()->mBoxOrdinal; +} + +template <> +bool ReverseCSSOrderAwareFrameIterator::IsForward() const { + return false; +} + +template <> +nsFrameList::reverse_iterator ReverseCSSOrderAwareFrameIterator::begin( + const nsFrameList& aList) { + return aList.rbegin(); +} + +template <> +nsFrameList::reverse_iterator ReverseCSSOrderAwareFrameIterator::end( + const nsFrameList& aList) { + return aList.rend(); +} + +} // namespace mozilla diff --git a/layout/generic/CSSOrderAwareFrameIterator.h b/layout/generic/CSSOrderAwareFrameIterator.h new file mode 100644 index 0000000000..8ad5cb65b2 --- /dev/null +++ b/layout/generic/CSSOrderAwareFrameIterator.h @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Iterator class for frame lists that respect CSS "order" during layout */ + +#ifndef mozilla_CSSOrderAwareFrameIterator_h +#define mozilla_CSSOrderAwareFrameIterator_h + +#include +#include "nsFrameList.h" +#include "nsIFrame.h" +#include "mozilla/Maybe.h" +#include "mozilla/Assertions.h" + +namespace mozilla { + +/** + * CSSOrderAwareFrameIteratorT is a base class for iterators that traverse + * child frame lists in a way that respects their CSS "order" property. + * https://drafts.csswg.org/css-flexbox-1/#order-property + * This class isn't meant to be directly used; instead, use its specializations + * CSSOrderAwareFrameIterator and ReverseCSSOrderAwareFrameIterator. + * + * Client code can use a CSSOrderAwareFrameIterator to traverse lower-"order" + * frames before higher-"order" ones (as required for correct flex/grid + * layout), without modifying the frames' actual ordering within the frame + * tree. Any frames with equal "order" values will be traversed consecutively, + * in frametree order (which is generally equivalent to DOM order). + * + * By default, the iterator will skip past placeholder frames during + * iteration. You can adjust this behavior via the ChildFilter constructor arg. + * + * By default, the iterator will use the frames' CSS "order" property to + * determine its traversal order. However, it can be customized to instead use + * the (prefixed) legacy "box-ordinal-group" CSS property instead, as part of + * emulating "display:-webkit-box" containers. This behavior can be customized + * using the OrderingProperty constructor arg. + * + * A few notes on performance: + * - If you're iterating multiple times in a row, it's a good idea to reuse + * the same iterator (calling Reset() to start each new iteration), rather than + * instantiating a new one each time. + * - If you have foreknowledge of the list's orderedness, you can save some + * time by passing eKnownOrdered or eKnownUnordered to the constructor (which + * will skip some checks during construction). + * + * Warning: if the given frame list changes, it makes the iterator invalid and + * bad things will happen if it's used further. + */ +template +class CSSOrderAwareFrameIteratorT { + public: + enum class OrderState { Unknown, Ordered, Unordered }; + enum class ChildFilter { SkipPlaceholders, IncludeAll }; + enum class OrderingProperty { + Order, // Default behavior: use "order". + BoxOrdinalGroup // Legacy behavior: use prefixed "box-ordinal-group". + }; + CSSOrderAwareFrameIteratorT( + nsIFrame* aContainer, FrameChildListID aListID, + ChildFilter aFilter = ChildFilter::SkipPlaceholders, + OrderState aState = OrderState::Unknown, + OrderingProperty aOrderProp = OrderingProperty::Order) + : mChildren(aContainer->GetChildList(aListID)), + mArrayIndex(0), + mItemIndex(0), + mSkipPlaceholders(aFilter == ChildFilter::SkipPlaceholders) +#ifdef DEBUG + , + mContainer(aContainer), + mListID(aListID) +#endif + { + MOZ_ASSERT(CanUse(aContainer), + "Only use this iterator in a container that honors 'order'"); + + size_t count = 0; + bool isOrdered = aState != OrderState::Unordered; + if (aState == OrderState::Unknown) { + auto maxOrder = std::numeric_limits::min(); + for (auto* child : mChildren) { + ++count; + + int32_t order = aOrderProp == OrderingProperty::BoxOrdinalGroup + ? child->StyleXUL()->mBoxOrdinal + : child->StylePosition()->mOrder; + + if (order < maxOrder) { + isOrdered = false; + break; + } + maxOrder = order; + } + } + if (isOrdered) { + mIter.emplace(begin(mChildren)); + mIterEnd.emplace(end(mChildren)); + } else { + count *= 2; // XXX somewhat arbitrary estimate for now... + mArray.emplace(count); + for (Iterator i(begin(mChildren)), iEnd(end(mChildren)); i != iEnd; ++i) { + mArray->AppendElement(*i); + } + auto comparator = aOrderProp == OrderingProperty::BoxOrdinalGroup + ? CSSBoxOrdinalGroupComparator + : CSSOrderComparator; + mArray->StableSort(comparator); + } + + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + + CSSOrderAwareFrameIteratorT(CSSOrderAwareFrameIteratorT&&) = default; + + ~CSSOrderAwareFrameIteratorT() { + MOZ_ASSERT(IsForward() == mItemCount.isNothing()); + } + + bool IsForward() const; + + nsIFrame* get() const { + MOZ_ASSERT(!AtEnd()); + if (mIter.isSome()) { + return **mIter; + } + return (*mArray)[mArrayIndex]; + } + + nsIFrame* operator*() const { return get(); } + + /** + * Return the child index of the current item, placeholders not counted. + * It's forbidden to call this method when the current frame is placeholder. + */ + size_t ItemIndex() const { + MOZ_ASSERT(!AtEnd()); + MOZ_ASSERT(!(**this)->IsPlaceholderFrame(), + "MUST not call this when at a placeholder"); + MOZ_ASSERT(IsForward() || mItemIndex < *mItemCount, + "Returning an out-of-range mItemIndex..."); + return mItemIndex; + } + + void SetItemCount(size_t aItemCount) { + MOZ_ASSERT(mIter.isSome() || aItemCount <= mArray->Length(), + "item count mismatch"); + mItemCount.emplace(aItemCount); + // Note: it's OK if mItemIndex underflows -- ItemIndex() + // will not be called unless there is at least one item. + mItemIndex = IsForward() ? 0 : *mItemCount - 1; + } + + /** + * Skip over placeholder children. + */ + void SkipPlaceholders() { + if (mIter.isSome()) { + for (; *mIter != *mIterEnd; ++*mIter) { + nsIFrame* child = **mIter; + if (!child->IsPlaceholderFrame()) { + return; + } + } + } else { + for (; mArrayIndex < mArray->Length(); ++mArrayIndex) { + nsIFrame* child = (*mArray)[mArrayIndex]; + if (!child->IsPlaceholderFrame()) { + return; + } + } + } + } + + bool AtEnd() const { + MOZ_ASSERT(mIter.isSome() || mArrayIndex <= mArray->Length()); + return mIter ? (*mIter == *mIterEnd) : mArrayIndex >= mArray->Length(); + } + + void Next() { +#ifdef DEBUG + MOZ_ASSERT(!AtEnd()); + const nsFrameList& list = mContainer->GetChildList(mListID); + MOZ_ASSERT(list.FirstChild() == mChildren.FirstChild() && + list.LastChild() == mChildren.LastChild(), + "the list of child frames must not change while iterating!"); +#endif + if (mSkipPlaceholders || !(**this)->IsPlaceholderFrame()) { + IsForward() ? ++mItemIndex : --mItemIndex; + } + if (mIter.isSome()) { + ++*mIter; + } else { + ++mArrayIndex; + } + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + + void Reset(ChildFilter aFilter = ChildFilter::SkipPlaceholders) { + if (mIter.isSome()) { + mIter.reset(); + mIter.emplace(begin(mChildren)); + mIterEnd.reset(); + mIterEnd.emplace(end(mChildren)); + } else { + mArrayIndex = 0; + } + mItemIndex = IsForward() ? 0 : *mItemCount - 1; + mSkipPlaceholders = aFilter == ChildFilter::SkipPlaceholders; + if (mSkipPlaceholders) { + SkipPlaceholders(); + } + } + + bool IsValid() const { return mIter.isSome() || mArray.isSome(); } + + void Invalidate() { + mIter.reset(); + mArray.reset(); + } + + bool ItemsAreAlreadyInOrder() const { return mIter.isSome(); } + + private: + static bool CanUse(const nsIFrame*); + + Iterator begin(const nsFrameList& aList); + Iterator end(const nsFrameList& aList); + + static int CSSOrderComparator(nsIFrame* const& a, nsIFrame* const& b); + static int CSSBoxOrdinalGroupComparator(nsIFrame* const& a, + nsIFrame* const& b); + + const nsFrameList& mChildren; + // Used if child list is already in ascending 'order'. + Maybe mIter; + Maybe mIterEnd; + // Used if child list is *not* in ascending 'order'. + // This array is pre-sorted in reverse order for a reverse iterator. + Maybe> mArray; + size_t mArrayIndex; + // The index of the current item (placeholders excluded). + size_t mItemIndex; + // The number of items (placeholders excluded). + // It's only initialized and used in a reverse iterator. + Maybe mItemCount; + // Skip placeholder children in the iteration? + bool mSkipPlaceholders; +#ifdef DEBUG + nsIFrame* mContainer; + FrameChildListID mListID; +#endif +}; + +using CSSOrderAwareFrameIterator = + CSSOrderAwareFrameIteratorT; +using ReverseCSSOrderAwareFrameIterator = + CSSOrderAwareFrameIteratorT; + +} // namespace mozilla + +#endif // mozilla_CSSOrderAwareFrameIterator_h diff --git a/layout/generic/ColumnSetWrapperFrame.cpp b/layout/generic/ColumnSetWrapperFrame.cpp new file mode 100644 index 0000000000..0f142ef4f3 --- /dev/null +++ b/layout/generic/ColumnSetWrapperFrame.cpp @@ -0,0 +1,315 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "ColumnSetWrapperFrame.h" + +#include "mozilla/ColumnUtils.h" +#include "mozilla/PresShell.h" +#include "nsContentUtils.h" +#include "nsIFrame.h" +#include "nsIFrameInlines.h" + +using namespace mozilla; + +nsBlockFrame* NS_NewColumnSetWrapperFrame(PresShell* aPresShell, + ComputedStyle* aStyle, + nsFrameState aStateFlags) { + ColumnSetWrapperFrame* frame = new (aPresShell) + ColumnSetWrapperFrame(aStyle, aPresShell->GetPresContext()); + + // CSS Multi-column level 1 section 2: A multi-column container + // establishes a new block formatting context, as per CSS 2.1 section + // 9.4.1. + frame->AddStateBits(aStateFlags | NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS); + return frame; +} + +NS_IMPL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame) + +NS_QUERYFRAME_HEAD(ColumnSetWrapperFrame) + NS_QUERYFRAME_ENTRY(ColumnSetWrapperFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) + +ColumnSetWrapperFrame::ColumnSetWrapperFrame(ComputedStyle* aStyle, + nsPresContext* aPresContext) + : nsBlockFrame(aStyle, aPresContext, kClassID) {} + +void ColumnSetWrapperFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) { + nsBlockFrame::Init(aContent, aParent, aPrevInFlow); + + // ColumnSetWrapperFrame doesn't need to call ResolveBidi(). + RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); +} + +nsContainerFrame* ColumnSetWrapperFrame::GetContentInsertionFrame() { + nsIFrame* columnSet = PrincipalChildList().OnlyChild(); + if (columnSet) { + // We have only one child, which means we don't have any column-span + // descendants. Thus we can safely return our only ColumnSet child's + // insertion frame as ours. + MOZ_ASSERT(columnSet->IsColumnSetFrame()); + return columnSet->GetContentInsertionFrame(); + } + + // We have column-span descendants. Return ourselves as the insertion + // frame to let nsCSSFrameConstructor::WipeContainingBlock() figure out + // what to do. + return this; +} + +void ColumnSetWrapperFrame::AppendDirectlyOwnedAnonBoxes( + nsTArray& aResult) { + MOZ_ASSERT(!GetPrevContinuation(), + "Who set NS_FRAME_OWNS_ANON_BOXES on our continuations?"); + + // It's sufficient to append the first ColumnSet child, which is the first + // continuation of all the other ColumnSets. + // + // We don't need to append -moz-column-span-wrapper children because + // they're non-inheriting anon boxes, and they cannot have any directly + // owned anon boxes nor generate any native anonymous content themselves. + // Thus, no need to restyle them. AssertColumnSpanWrapperSubtreeIsSane() + // asserts all the conditions above which allow us to skip appending + // -moz-column-span-wrappers. + auto FindFirstChildInChildLists = [this]() -> nsIFrame* { + const ChildListID listIDs[] = {FrameChildListID::Principal, + FrameChildListID::Overflow}; + for (nsIFrame* frag = this; frag; frag = frag->GetNextInFlow()) { + for (ChildListID id : listIDs) { + const nsFrameList& list = frag->GetChildList(id); + if (nsIFrame* firstChild = list.FirstChild()) { + return firstChild; + } + } + } + return nullptr; + }; + + nsIFrame* columnSet = FindFirstChildInChildLists(); + MOZ_ASSERT(columnSet && columnSet->IsColumnSetFrame(), + "The first child should always be ColumnSet!"); + aResult.AppendElement(OwnedAnonBox(columnSet)); +} + +#ifdef DEBUG_FRAME_DUMP +nsresult ColumnSetWrapperFrame::GetFrameName(nsAString& aResult) const { + return MakeFrameName(u"ColumnSetWrapper"_ns, aResult); +} +#endif + +// Disallow any append, insert, or remove operations after building the +// column hierarchy since any change to the column hierarchy in the column +// sub-tree need to be re-created. +void ColumnSetWrapperFrame::AppendFrames(ChildListID aListID, + nsFrameList&& aFrameList) { +#ifdef DEBUG + MOZ_ASSERT(!mFinishedBuildingColumns, "Should only call once!"); + mFinishedBuildingColumns = true; +#endif + + nsBlockFrame::AppendFrames(aListID, std::move(aFrameList)); + +#ifdef DEBUG + nsIFrame* firstColumnSet = PrincipalChildList().FirstChild(); + for (nsIFrame* child : PrincipalChildList()) { + if (child->IsColumnSpan()) { + AssertColumnSpanWrapperSubtreeIsSane(child); + } else if (child != firstColumnSet) { + // All the other ColumnSets are the continuation of the first ColumnSet. + MOZ_ASSERT(child->IsColumnSetFrame() && child->GetPrevContinuation(), + "ColumnSet's prev-continuation is not set properly?"); + } + } +#endif +} + +void ColumnSetWrapperFrame::InsertFrames( + ChildListID aListID, nsIFrame* aPrevFrame, + const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) { + MOZ_ASSERT_UNREACHABLE("Unsupported operation!"); + nsBlockFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine, + std::move(aFrameList)); +} + +void ColumnSetWrapperFrame::RemoveFrame(DestroyContext& aContext, + ChildListID aListID, + nsIFrame* aOldFrame) { + MOZ_ASSERT_UNREACHABLE("Unsupported operation!"); + nsBlockFrame::RemoveFrame(aContext, aListID, aOldFrame); +} + +void ColumnSetWrapperFrame::MarkIntrinsicISizesDirty() { + nsBlockFrame::MarkIntrinsicISizesDirty(); + + // The parent's method adds NS_BLOCK_NEEDS_BIDI_RESOLUTION to all our + // continuations. Clear the bit because we don't want to call ResolveBidi(). + for (nsIFrame* f = FirstContinuation(); f; f = f->GetNextContinuation()) { + f->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); + } +} + +nscoord ColumnSetWrapperFrame::GetMinISize(gfxContext* aRenderingContext) { + nscoord iSize = 0; + DISPLAY_MIN_INLINE_SIZE(this, iSize); + + if (Maybe containISize = + ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) { + // If we're size-contained in inline axis and contain-intrinsic-inline-size + // is not 'none', then use that size. + if (*containISize != NS_UNCONSTRAINEDSIZE) { + return *containISize; + } + + // In the 'none' case, we determine our minimum intrinsic size purely from + // our column styling, as if we had no descendants. This should match what + // happens in nsColumnSetFrame::GetMinISize in an actual no-descendants + // scenario. + const nsStyleColumn* colStyle = StyleColumn(); + if (colStyle->mColumnWidth.IsLength()) { + // As available inline size reduces to zero, our number of columns reduces + // to one, so no column gaps contribute to our minimum intrinsic size. + // Also, column-width doesn't set a lower bound on our minimum intrinsic + // size, either. Just use 0 because we're size-contained. + iSize = 0; + } else { + MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto, + "column-count and column-width can't both be auto!"); + // As available inline size reduces to zero, we still have mColumnCount + // columns, so compute our minimum intrinsic size based on N zero-width + // columns, with specified gap size between them. + const nscoord colGap = + ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE); + iSize = ColumnUtils::IntrinsicISize(colStyle->mColumnCount, colGap, 0); + } + } else { + for (nsIFrame* f : PrincipalChildList()) { + iSize = std::max(iSize, f->GetMinISize(aRenderingContext)); + } + } + + return iSize; +} + +nscoord ColumnSetWrapperFrame::GetPrefISize(gfxContext* aRenderingContext) { + nscoord iSize = 0; + DISPLAY_PREF_INLINE_SIZE(this, iSize); + + if (Maybe containISize = + ContainIntrinsicISize(NS_UNCONSTRAINEDSIZE)) { + if (*containISize != NS_UNCONSTRAINEDSIZE) { + return *containISize; + } + + const nsStyleColumn* colStyle = StyleColumn(); + nscoord colISize; + if (colStyle->mColumnWidth.IsLength()) { + colISize = + ColumnUtils::ClampUsedColumnWidth(colStyle->mColumnWidth.AsLength()); + } else { + MOZ_ASSERT(colStyle->mColumnCount != nsStyleColumn::kColumnCountAuto, + "column-count and column-width can't both be auto!"); + colISize = 0; + } + + // If column-count is auto, assume one column. + const uint32_t numColumns = + colStyle->mColumnCount == nsStyleColumn::kColumnCountAuto + ? 1 + : colStyle->mColumnCount; + const nscoord colGap = + ColumnUtils::GetColumnGap(this, NS_UNCONSTRAINEDSIZE); + iSize = ColumnUtils::IntrinsicISize(numColumns, colGap, colISize); + } else { + for (nsIFrame* f : PrincipalChildList()) { + iSize = std::max(iSize, f->GetPrefISize(aRenderingContext)); + } + } + + return iSize; +} + +template +Maybe ColumnSetWrapperFrame::GetBaselineBOffset( + Iterator aStart, Iterator aEnd, WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + BaselineExportContext aExportContext) const { + // Either forward iterator + first baseline, or reverse iterator + last + // baseline + MOZ_ASSERT((*aStart == PrincipalChildList().FirstChild() && + aBaselineGroup == BaselineSharingGroup::First) || + (*aStart == PrincipalChildList().LastChild() && + aBaselineGroup == BaselineSharingGroup::Last), + "Iterator direction must match baseline sharing group."); + if (StyleDisplay()->IsContainLayout()) { + return Nothing{}; + } + + // Start from start/end of principal child list, and use the first valid + // baseline. + for (auto itr = aStart; itr != aEnd; ++itr) { + const nsIFrame* kid = *itr; + auto kidBaseline = + kid->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext); + if (!kidBaseline) { + continue; + } + // Baseline is offset from the kid's rectangle, so find the offset to the + // kid's rectangle. + LogicalRect kidRect{aWM, kid->GetLogicalNormalPosition(aWM, GetSize()), + kid->GetLogicalSize(aWM)}; + if (aBaselineGroup == BaselineSharingGroup::First) { + *kidBaseline += kidRect.BStart(aWM); + } else { + *kidBaseline += (GetLogicalSize().BSize(aWM) - kidRect.BEnd(aWM)); + } + return kidBaseline; + } + return Nothing{}; +} + +Maybe ColumnSetWrapperFrame::GetNaturalBaselineBOffset( + WritingMode aWM, BaselineSharingGroup aBaselineGroup, + BaselineExportContext aExportContext) const { + if (aBaselineGroup == BaselineSharingGroup::First) { + return GetBaselineBOffset(PrincipalChildList().cbegin(), + PrincipalChildList().cend(), aWM, aBaselineGroup, + aExportContext); + } + return GetBaselineBOffset(PrincipalChildList().crbegin(), + PrincipalChildList().crend(), aWM, aBaselineGroup, + aExportContext); +} + +#ifdef DEBUG + +/* static */ +void ColumnSetWrapperFrame::AssertColumnSpanWrapperSubtreeIsSane( + const nsIFrame* aFrame) { + MOZ_ASSERT(aFrame->IsColumnSpan(), "aFrame is not column-span?"); + + if (!nsLayoutUtils::GetStyleFrame(const_cast(aFrame)) + ->Style() + ->IsAnonBox()) { + // aFrame's style frame has "column-span: all". Traverse no further. + return; + } + + MOZ_ASSERT( + aFrame->Style()->GetPseudoType() == PseudoStyleType::columnSpanWrapper, + "aFrame should be ::-moz-column-span-wrapper"); + + MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES), + "::-moz-column-span-wrapper anonymous blocks cannot own " + "other types of anonymous blocks!"); + + for (const nsIFrame* child : aFrame->PrincipalChildList()) { + AssertColumnSpanWrapperSubtreeIsSane(child); + } +} + +#endif diff --git a/layout/generic/ColumnSetWrapperFrame.h b/layout/generic/ColumnSetWrapperFrame.h new file mode 100644 index 0000000000..e219348189 --- /dev/null +++ b/layout/generic/ColumnSetWrapperFrame.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// A frame for CSS multi-column layout that wraps nsColumnSetFrames and +// column-span frames. + +#ifndef mozilla_ColumnSetWrapperFrame_h +#define mozilla_ColumnSetWrapperFrame_h + +#include "nsBlockFrame.h" + +namespace mozilla { + +class PresShell; + +// This class is a wrapper for nsColumnSetFrames and column-span frame. +// Essentially, we divide the *original* nsColumnSetFrame into multiple +// nsColumnSetFrames on the basis of the number and position of spanning +// elements. +// +// This wrapper is necessary for implementing column-span as it allows us to +// maintain each nsColumnSetFrame as an independent set of columns, and each +// column-span element then becomes just a block level element. +// +class ColumnSetWrapperFrame final : public nsBlockFrame { + public: + NS_DECL_FRAMEARENA_HELPERS(ColumnSetWrapperFrame) + NS_DECL_QUERYFRAME + + friend nsBlockFrame* ::NS_NewColumnSetWrapperFrame( + mozilla::PresShell* aPresShell, ComputedStyle* aStyle, + nsFrameState aStateFlags); + + void Init(nsIContent* aContent, nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) override; + + nsContainerFrame* GetContentInsertionFrame() override; + + void AppendDirectlyOwnedAnonBoxes(nsTArray& aResult) override; + +#ifdef DEBUG_FRAME_DUMP + nsresult GetFrameName(nsAString& aResult) const override; +#endif + + void AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) override; + + void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, + const nsLineList::iterator* aPrevFrameLine, + nsFrameList&& aFrameList) override; + + void RemoveFrame(DestroyContext&, ChildListID, nsIFrame*) override; + + void MarkIntrinsicISizesDirty() override; + + nscoord GetMinISize(gfxContext* aRenderingContext) override; + + nscoord GetPrefISize(gfxContext* aRenderingContext) override; + + Maybe GetNaturalBaselineBOffset( + WritingMode aWM, BaselineSharingGroup aBaselineGroup, + BaselineExportContext aExportContext) const override; + + private: + explicit ColumnSetWrapperFrame(ComputedStyle* aStyle, + nsPresContext* aPresContext); + ~ColumnSetWrapperFrame() override = default; + +#ifdef DEBUG + static void AssertColumnSpanWrapperSubtreeIsSane(const nsIFrame* aFrame); + + // True if frame constructor has finished building this frame and all of + // its descendants. + bool mFinishedBuildingColumns = false; +#endif + + template + Maybe GetBaselineBOffset(Iterator aStart, Iterator aEnd, + WritingMode aWM, + BaselineSharingGroup aBaselineGroup, + BaselineExportContext aExportContext) const; +}; + +} // namespace mozilla + +#endif // mozilla_ColumnSetWrapperFrame_h diff --git a/layout/generic/ColumnUtils.cpp b/layout/generic/ColumnUtils.cpp new file mode 100644 index 0000000000..6f1f54824f --- /dev/null +++ b/layout/generic/ColumnUtils.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A namespace class for static muti-column utilities. */ + +#include "mozilla/ColumnUtils.h" + +#include + +#include "nsContainerFrame.h" +#include "nsLayoutUtils.h" + +namespace mozilla { + +/* static */ +nscoord ColumnUtils::GetColumnGap(const nsContainerFrame* aFrame, + nscoord aPercentageBasis) { + const auto& columnGap = aFrame->StylePosition()->mColumnGap; + if (columnGap.IsNormal()) { + return aFrame->StyleFont()->mFont.size.ToAppUnits(); + } + return nsLayoutUtils::ResolveGapToLength(columnGap, aPercentageBasis); +} + +/* static */ +nscoord ColumnUtils::ClampUsedColumnWidth(const Length& aColumnWidth) { + // Per spec, used values will be clamped to a minimum of 1px. + return std::max(CSSPixel::ToAppUnits(1), aColumnWidth.ToAppUnits()); +} + +/* static */ +nscoord ColumnUtils::IntrinsicISize(uint32_t aColCount, nscoord aColGap, + nscoord aColISize) { + MOZ_ASSERT(aColCount > 0, "Cannot compute with zero columns!"); + + // Column box's inline-size times number of columns (n), plus n-1 column gaps. + nscoord iSize = aColISize * aColCount + aColGap * (aColCount - 1); + + // The multiplication above can make 'iSize' negative (integer overflow), + // so use std::max to protect against that. + return std::max(iSize, aColISize); +} + +} // namespace mozilla diff --git a/layout/generic/ColumnUtils.h b/layout/generic/ColumnUtils.h new file mode 100644 index 0000000000..15fa0efca5 --- /dev/null +++ b/layout/generic/ColumnUtils.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A namespace class for static muti-column utilities. */ + +#ifndef mozilla_ColumnUtils_h +#define mozilla_ColumnUtils_h + +#include "nsCoord.h" +#include "nsStyleConsts.h" + +class nsContainerFrame; + +namespace mozilla { + +// ColumnUtils is a namespace class containing utility functions used by +// multi-column containers like ColumnSetWrapperFrame and nsColumnSetFrame. +// +class ColumnUtils final { + public: + // Compute used value of 'column-gap' for aFrame. + static nscoord GetColumnGap(const nsContainerFrame* aFrame, + nscoord aPercentageBasis); + + // Clamp used column width to a minimum of 1px. + static nscoord ClampUsedColumnWidth(const Length& aColumnWidth); + + // Compute the intrinsic inline-size of a column container, given a non-zero + // column-count, column gap, and column box's inline-size. + static nscoord IntrinsicISize(uint32_t aColCount, nscoord aColGap, + nscoord aColISize); +}; + +} // namespace mozilla + +#endif // mozilla_ColumnUtils_h diff --git a/layout/generic/FrameClass.py b/layout/generic/FrameClass.py new file mode 100644 index 0000000000..0fae9e115e --- /dev/null +++ b/layout/generic/FrameClass.py @@ -0,0 +1,28 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +# Leaf constants to pass to Frame's leafness argument. +LEAF = "Leaf" +NOT_LEAF = "NotLeaf" +DYNAMIC_LEAF = "DynamicLeaf" + + +class FrameClass: + def __init__(self, cls): + self.cls = cls + + +class Frame(FrameClass): + def __init__(self, cls, ty, leafness): + FrameClass.__init__(self, cls) + self.ty = ty + self.leafness = leafness + self.is_concrete = True + + +class AbstractFrame(FrameClass): + def __init__(self, cls): + FrameClass.__init__(self, cls) + self.is_concrete = False diff --git a/layout/generic/FrameClasses.py b/layout/generic/FrameClasses.py new file mode 100644 index 0000000000..1e8271f230 --- /dev/null +++ b/layout/generic/FrameClasses.py @@ -0,0 +1,164 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# Frame class definitions, used to generate FrameIdList.h and FrameTypeList.h + +from FrameClass import DYNAMIC_LEAF, LEAF, NOT_LEAF, AbstractFrame, Frame + +# See FrameClass.py and GenerateFrameLists.py for implementation details. +# The following is a list of all the frame classes, followed by the frame type, +# and whether they are a leaf. +# +# The frame type is somewhat arbitrary (could literally be anything) but for +# new frame class implementations it's probably a good idea to make it a unique +# string (maybe matching the frame name). +# +# See bug 1555477 for some related discussion about the whole Type() set-up. +FRAME_CLASSES = [ + Frame("BRFrame", "Br", LEAF), + Frame("nsBCTableCellFrame", "TableCell", NOT_LEAF), + Frame("nsBackdropFrame", "Backdrop", LEAF), + Frame("nsBlockFrame", "Block", NOT_LEAF), + Frame("nsCanvasFrame", "Canvas", NOT_LEAF), + Frame("nsCheckboxRadioFrame", "CheckboxRadio", LEAF), + Frame("nsColorControlFrame", "ColorControl", LEAF), + Frame("nsColumnSetFrame", "ColumnSet", NOT_LEAF), + Frame("ColumnSetWrapperFrame", "ColumnSetWrapper", NOT_LEAF), + Frame("nsComboboxControlFrame", "ComboboxControl", NOT_LEAF), + Frame("nsComboboxDisplayFrame", "ComboboxDisplay", NOT_LEAF), + Frame("nsContinuingTextFrame", "Text", LEAF), + Frame("nsDateTimeControlFrame", "DateTimeControl", NOT_LEAF), + Frame("nsFieldSetFrame", "FieldSet", NOT_LEAF), + Frame("nsFileControlFrame", "Block", LEAF), + Frame("FileControlLabelFrame", "Block", NOT_LEAF), + Frame("nsFirstLetterFrame", "Letter", NOT_LEAF), + Frame("nsFirstLineFrame", "Line", NOT_LEAF), + Frame("nsFlexContainerFrame", "FlexContainer", NOT_LEAF), + Frame("nsIFrame", "None", NOT_LEAF), + Frame("nsGfxButtonControlFrame", "GfxButtonControl", LEAF), + Frame("nsGridContainerFrame", "GridContainer", NOT_LEAF), + Frame("nsHTMLButtonControlFrame", "HTMLButtonControl", NOT_LEAF), + Frame("nsHTMLCanvasFrame", "HTMLCanvas", NOT_LEAF), + Frame("nsHTMLFramesetBlankFrame", "None", LEAF), + Frame("nsHTMLFramesetBorderFrame", "None", LEAF), + Frame("nsHTMLFramesetFrame", "FrameSet", LEAF), + Frame("nsHTMLScrollFrame", "Scroll", NOT_LEAF), + Frame("nsImageControlFrame", "ImageControl", LEAF), + Frame("nsImageFrame", "Image", DYNAMIC_LEAF), + Frame("nsInlineFrame", "Inline", NOT_LEAF), + Frame("nsListControlFrame", "ListControl", NOT_LEAF), + Frame("nsMathMLFrame", "None", NOT_LEAF), + Frame("nsMathMLmactionFrame", "None", NOT_LEAF), + Frame("nsMathMLmathBlockFrame", "Block", NOT_LEAF), + Frame("nsMathMLmathInlineFrame", "Inline", NOT_LEAF), + Frame("nsMathMLmencloseFrame", "None", NOT_LEAF), + Frame("nsMathMLmfracFrame", "None", NOT_LEAF), + Frame("nsMathMLmmultiscriptsFrame", "None", NOT_LEAF), + Frame("nsMathMLmoFrame", "None", NOT_LEAF), + Frame("nsMathMLmpaddedFrame", "None", NOT_LEAF), + Frame("nsMathMLmrootFrame", "None", NOT_LEAF), + Frame("nsMathMLmrowFrame", "None", NOT_LEAF), + Frame("nsMathMLmspaceFrame", "None", LEAF), + Frame("nsMathMLmsqrtFrame", "None", NOT_LEAF), + Frame("nsMathMLmtableFrame", "Table", NOT_LEAF), + Frame("nsMathMLmtableWrapperFrame", "TableWrapper", NOT_LEAF), + Frame("nsMathMLmtdFrame", "TableCell", NOT_LEAF), + Frame("nsMathMLmtdInnerFrame", "Block", NOT_LEAF), + Frame("nsMathMLmtrFrame", "TableRow", NOT_LEAF), + Frame("nsMathMLmunderoverFrame", "None", NOT_LEAF), + Frame("nsMathMLsemanticsFrame", "None", NOT_LEAF), + Frame("nsMathMLTokenFrame", "None", NOT_LEAF), + Frame("nsMenuPopupFrame", "MenuPopup", NOT_LEAF), + Frame("nsMeterFrame", "Meter", LEAF), + Frame("nsNumberControlFrame", "TextInput", LEAF), + Frame("nsPageBreakFrame", "PageBreak", LEAF), + Frame("nsPageContentFrame", "PageContent", NOT_LEAF), + Frame("nsPageFrame", "Page", NOT_LEAF), + Frame("nsPlaceholderFrame", "Placeholder", LEAF), + Frame("nsProgressFrame", "Progress", LEAF), + Frame("nsRangeFrame", "Range", LEAF), + Frame("nsRubyBaseContainerFrame", "RubyBaseContainer", NOT_LEAF), + Frame("nsRubyBaseFrame", "RubyBase", NOT_LEAF), + Frame("nsRubyFrame", "Ruby", NOT_LEAF), + Frame("nsRubyTextContainerFrame", "RubyTextContainer", NOT_LEAF), + Frame("nsRubyTextFrame", "RubyText", NOT_LEAF), + Frame("SimpleXULLeafFrame", "SimpleXULLeaf", LEAF), + Frame("nsScrollbarButtonFrame", "SimpleXULLeaf", LEAF), + Frame("nsScrollbarFrame", "Scrollbar", NOT_LEAF), + Frame("nsSearchControlFrame", "SearchControl", LEAF), + Frame("nsSelectsAreaFrame", "Block", NOT_LEAF), + Frame("nsPageSequenceFrame", "PageSequence", NOT_LEAF), + Frame("nsSliderFrame", "Slider", NOT_LEAF), + Frame("nsSplitterFrame", "SimpleXULLeaf", NOT_LEAF), + Frame("nsSubDocumentFrame", "SubDocument", LEAF), + Frame("PrintedSheetFrame", "PrintedSheet", NOT_LEAF), + Frame("SVGAFrame", "SVGA", NOT_LEAF), + Frame("SVGClipPathFrame", "SVGClipPath", NOT_LEAF), + Frame("SVGContainerFrame", "None", NOT_LEAF), + Frame("SVGFEContainerFrame", "SVGFEContainer", NOT_LEAF), + Frame("SVGFEImageFrame", "SVGFEImage", LEAF), + Frame("SVGFELeafFrame", "SVGFELeaf", LEAF), + Frame("SVGFEUnstyledLeafFrame", "SVGFEUnstyledLeaf", LEAF), + Frame("SVGFilterFrame", "SVGFilter", NOT_LEAF), + Frame("SVGForeignObjectFrame", "SVGForeignObject", NOT_LEAF), + Frame("SVGGenericContainerFrame", "SVGGenericContainer", NOT_LEAF), + Frame("SVGGeometryFrame", "SVGGeometry", LEAF), + Frame("SVGGFrame", "SVGG", NOT_LEAF), + Frame("SVGImageFrame", "SVGImage", LEAF), + Frame("SVGInnerSVGFrame", "SVGInnerSVG", NOT_LEAF), + Frame("SVGLinearGradientFrame", "SVGLinearGradient", NOT_LEAF), + Frame("SVGMarkerFrame", "SVGMarker", NOT_LEAF), + Frame("SVGMarkerAnonChildFrame", "SVGMarkerAnonChild", NOT_LEAF), + Frame("SVGMaskFrame", "SVGMask", NOT_LEAF), + Frame("SVGOuterSVGFrame", "SVGOuterSVG", NOT_LEAF), + Frame("SVGOuterSVGAnonChildFrame", "SVGOuterSVGAnonChild", NOT_LEAF), + Frame("SVGPatternFrame", "SVGPattern", NOT_LEAF), + Frame("SVGRadialGradientFrame", "SVGRadialGradient", NOT_LEAF), + Frame("SVGStopFrame", "SVGStop", LEAF), + Frame("SVGSwitchFrame", "SVGSwitch", NOT_LEAF), + Frame("SVGSymbolFrame", "SVGSymbol", NOT_LEAF), + Frame("SVGTextFrame", "SVGText", NOT_LEAF), + # Not a leaf, though it always has a ShadowRoot, so in practice light DOM + # children never render. + Frame("SVGUseFrame", "SVGUse", NOT_LEAF), + Frame("MiddleCroppingLabelFrame", "MiddleCroppingLabel", LEAF), + Frame("SVGViewFrame", "SVGView", LEAF), + Frame("nsTableCellFrame", "TableCell", NOT_LEAF), + Frame("nsTableColFrame", "TableCol", LEAF), + Frame("nsTableColGroupFrame", "TableColGroup", NOT_LEAF), + Frame("nsTableFrame", "Table", NOT_LEAF), + Frame("nsTableWrapperFrame", "TableWrapper", NOT_LEAF), + Frame("nsTableRowFrame", "TableRow", NOT_LEAF), + Frame("nsTableRowGroupFrame", "TableRowGroup", NOT_LEAF), + Frame("nsTextControlFrame", "TextInput", LEAF), + Frame("nsTextFrame", "Text", LEAF), + Frame("nsTreeBodyFrame", "SimpleXULLeaf", LEAF), + Frame("nsVideoFrame", "HTMLVideo", NOT_LEAF), + Frame("ViewportFrame", "Viewport", NOT_LEAF), + Frame("WBRFrame", "Wbr", LEAF), + # Non-concrete classes (for FrameIID use) + AbstractFrame("MiddleCroppingBlockFrame"), + AbstractFrame("nsContainerFrame"), + AbstractFrame("nsLeafFrame"), + AbstractFrame("nsMathMLContainerFrame"), + AbstractFrame("nsRubyContentFrame"), + AbstractFrame("nsSplittableFrame"), + AbstractFrame("SVGDisplayContainerFrame"), + AbstractFrame("SVGGradientFrame"), + AbstractFrame("SVGPaintServerFrame"), + # Interfaces (for FrameIID use) + AbstractFrame("nsIAnonymousContentCreator"), + AbstractFrame("nsIFormControlFrame"), + AbstractFrame("nsIMathMLFrame"), + AbstractFrame("nsIPercentBSizeObserver"), + AbstractFrame("nsIPopupContainer"), + AbstractFrame("nsIScrollableFrame"), + AbstractFrame("nsIScrollbarMediator"), + AbstractFrame("nsISelectControlFrame"), + AbstractFrame("nsIStatefulFrame"), + AbstractFrame("ISVGDisplayableFrame"), + AbstractFrame("ISVGSVGFrame"), + AbstractFrame("nsITableCellLayout"), + AbstractFrame("nsITableLayout"), + AbstractFrame("nsITextControlFrame"), +] diff --git a/layout/generic/GenerateFrameLists.py b/layout/generic/GenerateFrameLists.py new file mode 100644 index 0000000000..af2c60922b --- /dev/null +++ b/layout/generic/GenerateFrameLists.py @@ -0,0 +1,41 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from FrameClasses import FRAME_CLASSES + +HEADER = "// THIS IS AUTOGENERATED BY GenerateFrameLists.py. DO NOT EDIT\n" + + +# Returns a list of list of FrameClass objects. The outermost list groups +# the FrameClasses by their frame type, and is sorted from the largest group +# to the smallest, and otherwise sorted by the frame type or class name. +def grouped_frame_classes(): + groups = dict() + for frame in FRAME_CLASSES: + if frame.is_concrete: + groups.setdefault(frame.ty, []).append(frame) + groups = groups.values() + return sorted(groups, key=lambda x: (-len(x), x[0].ty if len(x) > 1 else x[0].cls)) + + +def generate_frame_id_list_h(output, *ignore): + groups = grouped_frame_classes() + output.write(HEADER) + for group in groups: + for frame in group: + output.write( + "FRAME_ID(%s, %s, %s)\n" % (frame.cls, frame.ty, frame.leafness) + ) + for frame in FRAME_CLASSES: + if not frame.is_concrete: + output.write("ABSTRACT_FRAME_ID(%s)\n" % frame.cls) + + +def generate_frame_type_list_h(output, *ignore): + groups = grouped_frame_classes() + output.write(HEADER) + for group in groups: + output.write( + "FRAME_TYPE(%s, %s, %s)\n" % (group[0].ty, group[0].cls, group[-1].cls) + ) diff --git a/layout/generic/JustificationUtils.h b/layout/generic/JustificationUtils.h new file mode 100644 index 0000000000..346a8b6186 --- /dev/null +++ b/layout/generic/JustificationUtils.h @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_JustificationUtils_h_ +#define mozilla_JustificationUtils_h_ + +#include "mozilla/Attributes.h" +#include "nsCoord.h" + +namespace mozilla { + +/** + * Jutification Algorithm + * + * The justification algorithm is based on expansion opportunities + * between justifiable clusters. By this algorithm, there is one + * expansion opportunity at each side of a justifiable cluster, and + * at most one opportunity between two clusters. For example, if there + * is a line in a Chinese document is: "你好世界hello world", then + * the expansion opportunities (marked as '*') would be: + * + * 你*好*世*界*hello*' '*world + * + * The spacing left in a line will then be distributed equally to each + * opportunities. Because we want that, only justifiable clusters get + * expanded, and the split point between two justifiable clusters would + * be at the middle of the spacing, each expansion opportunities will be + * filled by two justification gaps. The example above would be: + * + * 你 | 好 | 世 | 界 |hello| ' ' |world + * + * In the algorithm, information about expansion opportunities is stored + * in structure JustificationInfo, and the assignment of justification + * gaps is in structure JustificationAssignment. + */ + +struct JustificationInfo { + // Number of expansion opportunities inside a span. It doesn't include + // any opportunities between this span and the one before or after. + int32_t mInnerOpportunities; + // The justifiability of the start and end sides of the span. + bool mIsStartJustifiable; + bool mIsEndJustifiable; + + constexpr JustificationInfo() + : mInnerOpportunities(0), + mIsStartJustifiable(false), + mIsEndJustifiable(false) {} + + // Claim that the last opportunity should be cancelled + // because the trailing space just gets trimmed. + void CancelOpportunityForTrimmedSpace() { + if (mInnerOpportunities > 0) { + mInnerOpportunities--; + } else { + // There is no inner opportunities, hence the whole frame must + // contain only the trimmed space, because any content before + // space would cause an inner opportunity. The space made each + // side justifiable, which should be cancelled now. + mIsStartJustifiable = false; + mIsEndJustifiable = false; + } + } +}; + +struct JustificationAssignment { + // There are at most 2 gaps per end, so it is enough to use 2 bits. + uint8_t mGapsAtStart : 2; + uint8_t mGapsAtEnd : 2; + + constexpr JustificationAssignment() : mGapsAtStart(0), mGapsAtEnd(0) {} + + int32_t TotalGaps() const { return mGapsAtStart + mGapsAtEnd; } +}; + +struct JustificationApplicationState { + struct { + // The total number of justification gaps to be processed. + int32_t mCount; + // The number of justification gaps which have been handled. + int32_t mHandled; + } mGaps; + + struct { + // The total spacing left in a line before justification. + nscoord mAvailable; + // The spacing has been consumed by handled justification gaps. + nscoord mConsumed; + } mWidth; + + JustificationApplicationState(int32_t aGaps, nscoord aWidth) { + mGaps.mCount = aGaps; + mGaps.mHandled = 0; + mWidth.mAvailable = aWidth; + mWidth.mConsumed = 0; + } + + bool IsJustifiable() const { + return mGaps.mCount > 0 && mWidth.mAvailable > 0; + } + + nscoord Consume(int32_t aGaps) { + mGaps.mHandled += aGaps; + nscoord newAllocate = (mWidth.mAvailable * mGaps.mHandled) / mGaps.mCount; + nscoord deltaWidth = newAllocate - mWidth.mConsumed; + mWidth.mConsumed = newAllocate; + return deltaWidth; + } +}; + +class JustificationUtils { + public: + // Compute justification gaps should be applied on a unit. + static int32_t CountGaps(const JustificationInfo& aInfo, + const JustificationAssignment& aAssign) { + // Justification gaps include two gaps for each inner opportunities + // and the gaps given assigned to the ends. + return aInfo.mInnerOpportunities * 2 + aAssign.TotalGaps(); + } +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_JustificationUtils_h_) */ diff --git a/layout/generic/LayoutMessageUtils.h b/layout/generic/LayoutMessageUtils.h new file mode 100644 index 0000000000..0cc694382f --- /dev/null +++ b/layout/generic/LayoutMessageUtils.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_ +#define LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_ + +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtils.h" +#include "nsIFrame.h" +#include "mozilla/AspectRatio.h" +#include "mozilla/webrender/WebRenderTypes.h" + +namespace IPC { + +template <> +struct ParamTraits { + using paramType = mozilla::IntrinsicSize; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.width); + WriteParam(aWriter, aParam.height); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->width) && + ReadParam(aReader, &aResult->height); + } +}; + +template <> +struct ParamTraits { + using paramType = mozilla::AspectRatio; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mRatio); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ReadParam(aReader, &aResult->mRatio); + } +}; + +template <> +struct ParamTraits + : public ContiguousEnumSerializerInclusive< + mozilla::StyleImageRendering, mozilla::StyleImageRendering::Auto, + mozilla::StyleImageRendering::Optimizequality> {}; + +} // namespace IPC + +#endif // LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_ diff --git a/layout/generic/MathMLTextRunFactory.cpp b/layout/generic/MathMLTextRunFactory.cpp new file mode 100644 index 0000000000..a04062ba80 --- /dev/null +++ b/layout/generic/MathMLTextRunFactory.cpp @@ -0,0 +1,679 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MathMLTextRunFactory.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/BinarySearch.h" +#include "mozilla/ComputedStyle.h" +#include "mozilla/ComputedStyleInlines.h" +#include "mozilla/StaticPrefs_mathml.h" +#include "mozilla/intl/UnicodeScriptCodes.h" + +#include "nsStyleConsts.h" +#include "nsTextFrameUtils.h" +#include "nsFontMetrics.h" +#include "nsDeviceContext.h" + +using namespace mozilla; + +/* + Entries for the mathvariant lookup tables. mKey represents the Unicode + character to be transformed and is used for searching the tables. + mReplacement represents the mapped mathvariant Unicode character. +*/ +typedef struct { + uint32_t mKey; + uint32_t mReplacement; +} MathVarMapping; + +/* + Lookup tables for use with mathvariant mappings to transform a unicode + character point to another unicode character that indicates the proper output. + mKey represents one of two concepts. + 1. In the Latin table it represents a hole in the mathematical alphanumeric + block, where the character that should occupy that position is located + elsewhere. + 2. It represents an Arabic letter. + + As a replacement, 0 is reserved to indicate no mapping was found. +*/ +static const MathVarMapping gArabicInitialMapTable[] = { + {0x628, 0x1EE21}, {0x62A, 0x1EE35}, {0x62B, 0x1EE36}, {0x62C, 0x1EE22}, + {0x62D, 0x1EE27}, {0x62E, 0x1EE37}, {0x633, 0x1EE2E}, {0x634, 0x1EE34}, + {0x635, 0x1EE31}, {0x636, 0x1EE39}, {0x639, 0x1EE2F}, {0x63A, 0x1EE3B}, + {0x641, 0x1EE30}, {0x642, 0x1EE32}, {0x643, 0x1EE2A}, {0x644, 0x1EE2B}, + {0x645, 0x1EE2C}, {0x646, 0x1EE2D}, {0x647, 0x1EE24}, {0x64A, 0x1EE29}}; + +static const MathVarMapping gArabicTailedMapTable[] = { + {0x62C, 0x1EE42}, {0x62D, 0x1EE47}, {0x62E, 0x1EE57}, {0x633, 0x1EE4E}, + {0x634, 0x1EE54}, {0x635, 0x1EE51}, {0x636, 0x1EE59}, {0x639, 0x1EE4F}, + {0x63A, 0x1EE5B}, {0x642, 0x1EE52}, {0x644, 0x1EE4B}, {0x646, 0x1EE4D}, + {0x64A, 0x1EE49}, {0x66F, 0x1EE5F}, {0x6BA, 0x1EE5D}}; + +static const MathVarMapping gArabicStretchedMapTable[] = { + {0x628, 0x1EE61}, {0x62A, 0x1EE75}, {0x62B, 0x1EE76}, {0x62C, 0x1EE62}, + {0x62D, 0x1EE67}, {0x62E, 0x1EE77}, {0x633, 0x1EE6E}, {0x634, 0x1EE74}, + {0x635, 0x1EE71}, {0x636, 0x1EE79}, {0x637, 0x1EE68}, {0x638, 0x1EE7A}, + {0x639, 0x1EE6F}, {0x63A, 0x1EE7B}, {0x641, 0x1EE70}, {0x642, 0x1EE72}, + {0x643, 0x1EE6A}, {0x645, 0x1EE6C}, {0x646, 0x1EE6D}, {0x647, 0x1EE64}, + {0x64A, 0x1EE69}, {0x66E, 0x1EE7C}, {0x6A1, 0x1EE7E}}; + +static const MathVarMapping gArabicLoopedMapTable[] = { + {0x627, 0x1EE80}, {0x628, 0x1EE81}, {0x62A, 0x1EE95}, {0x62B, 0x1EE96}, + {0x62C, 0x1EE82}, {0x62D, 0x1EE87}, {0x62E, 0x1EE97}, {0x62F, 0x1EE83}, + {0x630, 0x1EE98}, {0x631, 0x1EE93}, {0x632, 0x1EE86}, {0x633, 0x1EE8E}, + {0x634, 0x1EE94}, {0x635, 0x1EE91}, {0x636, 0x1EE99}, {0x637, 0x1EE88}, + {0x638, 0x1EE9A}, {0x639, 0x1EE8F}, {0x63A, 0x1EE9B}, {0x641, 0x1EE90}, + {0x642, 0x1EE92}, {0x644, 0x1EE8B}, {0x645, 0x1EE8C}, {0x646, 0x1EE8D}, + {0x647, 0x1EE84}, {0x648, 0x1EE85}, {0x64A, 0x1EE89}}; + +static const MathVarMapping gArabicDoubleMapTable[] = { + {0x628, 0x1EEA1}, {0x62A, 0x1EEB5}, {0x62B, 0x1EEB6}, {0x62C, 0x1EEA2}, + {0x62D, 0x1EEA7}, {0x62E, 0x1EEB7}, {0x62F, 0x1EEA3}, {0x630, 0x1EEB8}, + {0x631, 0x1EEB3}, {0x632, 0x1EEA6}, {0x633, 0x1EEAE}, {0x634, 0x1EEB4}, + {0x635, 0x1EEB1}, {0x636, 0x1EEB9}, {0x637, 0x1EEA8}, {0x638, 0x1EEBA}, + {0x639, 0x1EEAF}, {0x63A, 0x1EEBB}, {0x641, 0x1EEB0}, {0x642, 0x1EEB2}, + {0x644, 0x1EEAB}, {0x645, 0x1EEAC}, {0x646, 0x1EEAD}, {0x648, 0x1EEA5}, + {0x64A, 0x1EEA9}}; + +static const MathVarMapping gLatinExceptionMapTable[] = { + {0x1D455, 0x210E}, {0x1D49D, 0x212C}, {0x1D4A0, 0x2130}, {0x1D4A1, 0x2131}, + {0x1D4A3, 0x210B}, {0x1D4A4, 0x2110}, {0x1D4A7, 0x2112}, {0x1D4A8, 0x2133}, + {0x1D4AD, 0x211B}, {0x1D4BA, 0x212F}, {0x1D4BC, 0x210A}, {0x1D4C4, 0x2134}, + {0x1D506, 0x212D}, {0x1D50B, 0x210C}, {0x1D50C, 0x2111}, {0x1D515, 0x211C}, + {0x1D51D, 0x2128}, {0x1D53A, 0x2102}, {0x1D53F, 0x210D}, {0x1D545, 0x2115}, + {0x1D547, 0x2119}, {0x1D548, 0x211A}, {0x1D549, 0x211D}, {0x1D551, 0x2124}}; + +namespace { + +struct MathVarMappingWrapper { + const MathVarMapping* const mTable; + explicit MathVarMappingWrapper(const MathVarMapping* aTable) + : mTable(aTable) {} + uint32_t operator[](size_t index) const { return mTable[index].mKey; } +}; + +} // namespace + +// Finds a MathVarMapping struct with the specified key (aKey) within aTable. +// aTable must be an array, whose length is specified by aNumElements +static uint32_t MathvarMappingSearch(uint32_t aKey, + const MathVarMapping* aTable, + uint32_t aNumElements) { + size_t index; + if (BinarySearch(MathVarMappingWrapper(aTable), 0, aNumElements, aKey, + &index)) { + return aTable[index].mReplacement; + } + + return 0; +} + +#define GREEK_UPPER_THETA 0x03F4 +#define HOLE_GREEK_UPPER_THETA 0x03A2 +#define NABLA 0x2207 +#define PARTIAL_DIFFERENTIAL 0x2202 +#define GREEK_UPPER_ALPHA 0x0391 +#define GREEK_UPPER_OMEGA 0x03A9 +#define GREEK_LOWER_ALPHA 0x03B1 +#define GREEK_LOWER_OMEGA 0x03C9 +#define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5 +#define GREEK_THETA_SYMBOL 0x03D1 +#define GREEK_KAPPA_SYMBOL 0x03F0 +#define GREEK_PHI_SYMBOL 0x03D5 +#define GREEK_RHO_SYMBOL 0x03F1 +#define GREEK_PI_SYMBOL 0x03D6 +#define GREEK_LETTER_DIGAMMA 0x03DC +#define GREEK_SMALL_LETTER_DIGAMMA 0x03DD +#define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA +#define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB + +#define LATIN_SMALL_LETTER_DOTLESS_I 0x0131 +#define LATIN_SMALL_LETTER_DOTLESS_J 0x0237 + +#define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4 +#define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5 + +#define MATH_BOLD_UPPER_A 0x1D400 +#define MATH_ITALIC_UPPER_A 0x1D434 +#define MATH_BOLD_SMALL_A 0x1D41A +#define MATH_BOLD_UPPER_ALPHA 0x1D6A8 +#define MATH_BOLD_SMALL_ALPHA 0x1D6C2 +#define MATH_ITALIC_UPPER_ALPHA 0x1D6E2 +#define MATH_BOLD_DIGIT_ZERO 0x1D7CE +#define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8 + +#define MATH_BOLD_UPPER_THETA 0x1D6B9 +#define MATH_BOLD_NABLA 0x1D6C1 +#define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB +#define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC +#define MATH_BOLD_THETA_SYMBOL 0x1D6DD +#define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE +#define MATH_BOLD_PHI_SYMBOL 0x1D6DF +#define MATH_BOLD_RHO_SYMBOL 0x1D6E0 +#define MATH_BOLD_PI_SYMBOL 0x1D6E1 + +/* + Performs the character mapping needed to implement MathML's mathvariant + attribute. It takes a unicode character and maps it to its appropriate + mathvariant counterpart specified by aMathVar. The mapped character is + typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but + there are exceptions which this function accounts for. + Characters without a valid mapping or valid aMathvar value are returned + unaltered. Characters already in the mathematical blocks (or are one of the + exceptions) are never transformed. + Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h. + The transformable characters can be found at: + http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and + https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols +*/ +static uint32_t MathVariant(uint32_t aCh, StyleMathVariant aMathVar) { + uint32_t baseChar; + enum CharacterType { + kIsLatin, + kIsGreekish, + kIsNumber, + kIsArabic, + }; + CharacterType varType; + + int8_t multiplier; + + if (aMathVar <= StyleMathVariant::Normal) { + // nothing to do here + return aCh; + } + if (aMathVar > StyleMathVariant::Stretched) { + NS_ASSERTION(false, "Illegal mathvariant value"); + return aCh; + } + + // Exceptional characters with at most one possible transformation + if (aCh == HOLE_GREEK_UPPER_THETA) { + // Nothing at this code point is transformed + return aCh; + } + if (aCh == GREEK_LETTER_DIGAMMA) { + if (aMathVar == StyleMathVariant::Bold) { + return MATH_BOLD_CAPITAL_DIGAMMA; + } + return aCh; + } + if (aCh == GREEK_SMALL_LETTER_DIGAMMA) { + if (aMathVar == StyleMathVariant::Bold) { + return MATH_BOLD_SMALL_DIGAMMA; + } + return aCh; + } + if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) { + if (aMathVar == StyleMathVariant::Italic) { + return MATH_ITALIC_SMALL_DOTLESS_I; + } + return aCh; + } + if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) { + if (aMathVar == StyleMathVariant::Italic) { + return MATH_ITALIC_SMALL_DOTLESS_J; + } + return aCh; + } + + // The Unicode mathematical blocks are divided into four segments: Latin, + // Greek, numbers and Arabic. In the case of the first three + // baseChar represents the relative order in which the characters are + // encoded in the Unicode mathematical block, normalised to the first + // character of that sequence. + // + if ('A' <= aCh && aCh <= 'Z') { + baseChar = aCh - 'A'; + varType = kIsLatin; + } else if ('a' <= aCh && aCh <= 'z') { + // Lowercase characters are placed immediately after the uppercase + // characters in the Unicode mathematical block. The constant subtraction + // represents the number of characters between the start of the sequence + // (capital A) and the first lowercase letter. + baseChar = MATH_BOLD_SMALL_A - MATH_BOLD_UPPER_A + aCh - 'a'; + varType = kIsLatin; + } else if ('0' <= aCh && aCh <= '9') { + baseChar = aCh - '0'; + varType = kIsNumber; + } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) { + baseChar = aCh - GREEK_UPPER_ALPHA; + varType = kIsGreekish; + } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) { + // Lowercase Greek comes after uppercase Greek. + // Note in this instance the presence of an additional character (Nabla) + // between the end of the uppercase Greek characters and the lowercase + // ones. + baseChar = + MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA + aCh - GREEK_LOWER_ALPHA; + varType = kIsGreekish; + } else if (0x0600 <= aCh && aCh <= 0x06FF) { + // Arabic characters are defined within this range + varType = kIsArabic; + } else { + switch (aCh) { + case GREEK_UPPER_THETA: + baseChar = MATH_BOLD_UPPER_THETA - MATH_BOLD_UPPER_ALPHA; + break; + case NABLA: + baseChar = MATH_BOLD_NABLA - MATH_BOLD_UPPER_ALPHA; + break; + case PARTIAL_DIFFERENTIAL: + baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_LUNATE_EPSILON_SYMBOL: + baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_THETA_SYMBOL: + baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_KAPPA_SYMBOL: + baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_PHI_SYMBOL: + baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_RHO_SYMBOL: + baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + case GREEK_PI_SYMBOL: + baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA; + break; + default: + return aCh; + } + + varType = kIsGreekish; + } + + if (varType == kIsNumber) { + switch (aMathVar) { + // Each possible number mathvariant is encoded in a single, contiguous + // block. For example the beginning of the double struck number range + // follows immediately after the end of the bold number range. + // multiplier represents the order of the sequences relative to the first + // one. + case StyleMathVariant::Bold: + multiplier = 0; + break; + case StyleMathVariant::DoubleStruck: + multiplier = 1; + break; + case StyleMathVariant::SansSerif: + multiplier = 2; + break; + case StyleMathVariant::BoldSansSerif: + multiplier = 3; + break; + case StyleMathVariant::Monospace: + multiplier = 4; + break; + default: + // This mathvariant isn't defined for numbers or is otherwise normal + return aCh; + } + // As the ranges are contiguous, to find the desired mathvariant range it + // is sufficient to multiply the position within the sequence order + // (multiplier) with the period of the sequence (which is constant for all + // number sequences) and to add the character point of the first character + // within the number mathvariant range. + // To this the baseChar calculated earlier is added to obtain the final + // code point. + return baseChar + + multiplier * (MATH_DOUBLE_STRUCK_ZERO - MATH_BOLD_DIGIT_ZERO) + + MATH_BOLD_DIGIT_ZERO; + } else if (varType == kIsGreekish) { + switch (aMathVar) { + case StyleMathVariant::Bold: + multiplier = 0; + break; + case StyleMathVariant::Italic: + multiplier = 1; + break; + case StyleMathVariant::BoldItalic: + multiplier = 2; + break; + case StyleMathVariant::BoldSansSerif: + multiplier = 3; + break; + case StyleMathVariant::SansSerifBoldItalic: + multiplier = 4; + break; + default: + // This mathvariant isn't defined for Greek or is otherwise normal + return aCh; + } + // See the kIsNumber case for an explanation of the following calculation + return baseChar + MATH_BOLD_UPPER_ALPHA + + multiplier * (MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA); + } + + uint32_t tempChar; + uint32_t newChar; + if (varType == kIsArabic) { + const MathVarMapping* mapTable; + uint32_t tableLength; + switch (aMathVar) { + /* The Arabic mathematical block is not continuous, nor does it have a + * monotonic mapping to the unencoded characters, requiring the use of a + * lookup table. + */ + case StyleMathVariant::Initial: + mapTable = gArabicInitialMapTable; + tableLength = ArrayLength(gArabicInitialMapTable); + break; + case StyleMathVariant::Tailed: + mapTable = gArabicTailedMapTable; + tableLength = ArrayLength(gArabicTailedMapTable); + break; + case StyleMathVariant::Stretched: + mapTable = gArabicStretchedMapTable; + tableLength = ArrayLength(gArabicStretchedMapTable); + break; + case StyleMathVariant::Looped: + mapTable = gArabicLoopedMapTable; + tableLength = ArrayLength(gArabicLoopedMapTable); + break; + case StyleMathVariant::DoubleStruck: + mapTable = gArabicDoubleMapTable; + tableLength = ArrayLength(gArabicDoubleMapTable); + break; + default: + // No valid transformations exist + return aCh; + } + newChar = MathvarMappingSearch(aCh, mapTable, tableLength); + } else { + // Must be Latin + if (aMathVar > StyleMathVariant::Monospace) { + // Latin doesn't support the Arabic mathvariants + return aCh; + } + multiplier = uint8_t(aMathVar) - 2; + // This is possible because the values for StyleMathVariant::* are + // chosen to coincide with the order in which the encoded mathvariant + // characters are located within their unicode block (less an offset to + // avoid _NONE and _NORMAL variants) + // See the kIsNumber case for an explanation of the following calculation + tempChar = baseChar + MATH_BOLD_UPPER_A + + multiplier * (MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A); + // There are roughly twenty characters that are located outside of the + // mathematical block, so the spaces where they ought to be are used + // as keys for a lookup table containing the correct character mappings. + newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable, + ArrayLength(gLatinExceptionMapTable)); + } + + if (newChar) { + return newChar; + } else if (varType == kIsLatin) { + return tempChar; + } else { + // An Arabic character without a corresponding mapping + return aCh; + } +} + +#define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y') +#define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's') + +void MathMLTextRunFactory::RebuildTextRun( + nsTransformedTextRun* aTextRun, mozilla::gfx::DrawTarget* aRefDrawTarget, + gfxMissingFontRecorder* aMFR) { + gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); + + nsAutoString convertedString; + AutoTArray charsToMergeArray; + AutoTArray deletedCharsArray; + AutoTArray, 50> styleArray; + AutoTArray canBreakBeforeArray; + bool mergeNeeded = false; + + bool singleCharMI = + !!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSingleCharMi); + + uint32_t length = aTextRun->GetLength(); + const char16_t* str = aTextRun->mString.BeginReading(); + const nsTArray>& styles = aTextRun->mStyles; + nsFont font; + if (length) { + font = styles[0]->mFont; + + if (mSSTYScriptLevel || (mFlags & MATH_FONT_FEATURE_DTLS)) { + bool foundSSTY = false; + bool foundDTLS = false; + // We respect ssty settings explicitly set by the user + for (uint32_t i = 0; i < font.fontFeatureSettings.Length(); i++) { + if (font.fontFeatureSettings[i].mTag == TT_SSTY) { + foundSSTY = true; + } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) { + foundDTLS = true; + } + } + if (mSSTYScriptLevel && !foundSSTY) { + uint8_t sstyLevel = 0; + float scriptScaling = + pow(styles[0]->mScriptSizeMultiplier, mSSTYScriptLevel); + static_assert(kMathMLDefaultScriptSizeMultiplier < 1, + "Shouldn't it make things smaller?"); + /* + An SSTY level of 2 is set if the scaling factor is less than or equal + to halfway between that for a scriptlevel of 1 (0.71) and that of a + scriptlevel of 2 (0.71^2), assuming the default script size + multiplier. An SSTY level of 1 is set if the script scaling factor is + less than or equal that for a scriptlevel of 1 assuming the default + script size multiplier. + + User specified values of script size multiplier will change the + scaling factor which mSSTYScriptLevel values correspond to. + + In the event that the script size multiplier actually makes things + larger, no change is made. + + To opt out of this change, add the following to the stylesheet: + "font-feature-settings: 'ssty' 0" + */ + if (scriptScaling <= (kMathMLDefaultScriptSizeMultiplier + + (kMathMLDefaultScriptSizeMultiplier * + kMathMLDefaultScriptSizeMultiplier)) / + 2) { + // Currently only the first two ssty settings are used, so two is + // large as we go + sstyLevel = 2; + } else if (scriptScaling <= kMathMLDefaultScriptSizeMultiplier) { + sstyLevel = 1; + } + if (sstyLevel) { + gfxFontFeature settingSSTY; + settingSSTY.mTag = TT_SSTY; + settingSSTY.mValue = sstyLevel; + font.fontFeatureSettings.AppendElement(settingSSTY); + } + } + /* + Apply the dtls font feature setting (dotless). + This gets applied to the base frame and all descendants of the base + frame of certain and frames. + + See nsMathMLmunderoverFrame.cpp for a full description. + + To opt out of this change, add the following to the stylesheet: + "font-feature-settings: 'dtls' 0" + */ + if ((mFlags & MATH_FONT_FEATURE_DTLS) && !foundDTLS) { + gfxFontFeature settingDTLS; + settingDTLS.mTag = TT_DTLS; + settingDTLS.mValue = 1; + font.fontFeatureSettings.AppendElement(settingDTLS); + } + } + } + + StyleMathVariant mathVar = StyleMathVariant::None; + bool doMathvariantStyling = true; + + // Ensure it will be safe to call FindFontForChar in the loop below. + fontGroup->CheckForUpdatedPlatformList(); + + for (uint32_t i = 0; i < length; ++i) { + int extraChars = 0; + mathVar = styles[i]->mMathVariant; + + if (singleCharMI && mathVar == StyleMathVariant::None) { + mathVar = StyleMathVariant::Italic; + } + + uint32_t ch = str[i]; + if (i < length - 1 && NS_IS_SURROGATE_PAIR(ch, str[i + 1])) { + ch = SURROGATE_TO_UCS4(ch, str[i + 1]); + } + uint32_t ch2 = MathVariant(ch, mathVar); + + if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() && + (mathVar == StyleMathVariant::Bold || + mathVar == StyleMathVariant::BoldItalic || + mathVar == StyleMathVariant::Italic)) { + if (ch == ch2 && ch != 0x20 && ch != 0xA0) { + // Don't apply the CSS style if a character cannot be + // transformed. There is an exception for whitespace as it is both + // common and innocuous. + doMathvariantStyling = false; + } + if (ch2 != ch) { + // Bug 930504. Some platforms do not have fonts for Mathematical + // Alphanumeric Symbols. Hence we check whether the transformed + // character is actually available. + FontMatchType matchType; + RefPtr mathFont = fontGroup->FindFontForChar( + ch2, 0, 0, intl::Script::COMMON, nullptr, &matchType); + if (mathFont) { + // Don't apply the CSS style if there is a math font for at least one + // of the transformed character in this text run. + doMathvariantStyling = false; + } else { + // We fallback to the original character. + ch2 = ch; + if (aMFR) { + aMFR->RecordScript(intl::Script::MATHEMATICAL_NOTATION); + } + } + } + } + + deletedCharsArray.AppendElement(false); + charsToMergeArray.AppendElement(false); + styleArray.AppendElement(styles[i]); + canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); + + if (IS_IN_BMP(ch2)) { + convertedString.Append(ch2); + } else { + convertedString.Append(H_SURROGATE(ch2)); + convertedString.Append(L_SURROGATE(ch2)); + ++extraChars; + if (!IS_IN_BMP(ch)) { + deletedCharsArray.AppendElement( + true); // not exactly deleted, but + // the trailing surrogate is skipped + ++i; + } + } + + while (extraChars-- > 0) { + mergeNeeded = true; + charsToMergeArray.AppendElement(true); + styleArray.AppendElement(styles[i]); + canBreakBeforeArray.AppendElement(false); + } + } + + gfx::ShapedTextFlags flags; + gfxTextRunFactory::Parameters innerParams = + GetParametersForInner(aTextRun, &flags, aRefDrawTarget); + + RefPtr transformedChild; + RefPtr cachedChild; + gfxTextRun* child; + + if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() && + doMathvariantStyling) { + if (mathVar == StyleMathVariant::Bold) { + font.style = FontSlantStyle::NORMAL; + font.weight = FontWeight::BOLD; + } else if (mathVar == StyleMathVariant::Italic) { + font.style = FontSlantStyle::ITALIC; + font.weight = FontWeight::NORMAL; + } else if (mathVar == StyleMathVariant::BoldItalic) { + font.style = FontSlantStyle::ITALIC; + font.weight = FontWeight::BOLD; + } + } + gfxFontGroup* newFontGroup = nullptr; + + // Get the correct gfxFontGroup that corresponds to the earlier font changes. + if (length) { + font.size = font.size.ScaledBy(mFontInflation); + nsPresContext* pc = styles[0]->mPresContext; + nsFontMetrics::Params params; + params.language = styles[0]->mLanguage; + params.explicitLanguage = styles[0]->mExplicitLanguage; + params.userFontSet = pc->GetUserFontSet(); + params.textPerf = pc->GetTextPerfMetrics(); + params.featureValueLookup = pc->GetFontFeatureValuesLookup(); + RefPtr metrics = pc->GetMetricsFor(font, params); + newFontGroup = metrics->GetThebesFontGroup(); + } + + if (!newFontGroup) { + // If we can't get a new font group, fall back to the old one. Rendering + // will be incorrect, but not significantly so. + newFontGroup = fontGroup; + } + + if (mInnerTransformingTextRunFactory) { + transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( + convertedString.BeginReading(), convertedString.Length(), &innerParams, + newFontGroup, flags, nsTextFrameUtils::Flags(), std::move(styleArray), + false); + child = transformedChild.get(); + } else { + cachedChild = newFontGroup->MakeTextRun( + convertedString.BeginReading(), convertedString.Length(), &innerParams, + flags, nsTextFrameUtils::Flags(), aMFR); + child = cachedChild.get(); + } + if (!child) return; + + typedef gfxTextRun::Range Range; + + // Copy potential linebreaks into child so they're preserved + // (and also child will be shaped appropriately) + NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(), + "Dropped characters or break-before values somewhere!"); + Range range(0, uint32_t(canBreakBeforeArray.Length())); + child->SetPotentialLineBreaks(range, canBreakBeforeArray.Elements()); + if (transformedChild) { + transformedChild->FinishSettingProperties(aRefDrawTarget, aMFR); + } + + aTextRun->ResetGlyphRuns(); + if (mergeNeeded) { + // Now merge multiple characters into one multi-glyph character as required + NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(), + "source length mismatch"); + NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(), + "destination length mismatch"); + MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(), + deletedCharsArray.Elements()); + } else { + // No merging to do, so just copy; this produces a more optimized textrun. + // We can't steal the data because the child may be cached and stealing + // the data would break the cache. + aTextRun->CopyGlyphDataFrom(child, Range(child), 0); + } +} diff --git a/layout/generic/MathMLTextRunFactory.h b/layout/generic/MathMLTextRunFactory.h new file mode 100644 index 0000000000..79ef0dae16 --- /dev/null +++ b/layout/generic/MathMLTextRunFactory.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MATHMLTEXTRUNFACTORY_H_ +#define MATHMLTEXTRUNFACTORY_H_ + +#include "mozilla/UniquePtr.h" +#include "nsTextRunTransformations.h" + +/** + * Builds textruns that render their text with MathML specific renderings. + */ +class MathMLTextRunFactory : public nsTransformingTextRunFactory { + public: + MathMLTextRunFactory(mozilla::UniquePtr + aInnerTransformingTextRunFactory, + uint32_t aFlags, uint8_t aSSTYScriptLevel, + float aFontInflation) + : mInnerTransformingTextRunFactory( + std::move(aInnerTransformingTextRunFactory)), + mFlags(aFlags), + mFontInflation(aFontInflation), + mSSTYScriptLevel(aSSTYScriptLevel) {} + + virtual void RebuildTextRun(nsTransformedTextRun* aTextRun, + mozilla::gfx::DrawTarget* aRefDrawTarget, + gfxMissingFontRecorder* aMFR) override; + enum { + // Style effects which may override single character behaviour + MATH_FONT_FEATURE_DTLS = 0x4, // font feature dtls should be set + }; + + protected: + mozilla::UniquePtr + mInnerTransformingTextRunFactory; + uint32_t mFlags; + float mFontInflation; + uint8_t mSSTYScriptLevel; +}; + +#endif /*MATHMLTEXTRUNFACTORY_H_*/ diff --git a/layout/generic/MiddleCroppingBlockFrame.cpp b/layout/generic/MiddleCroppingBlockFrame.cpp new file mode 100644 index 0000000000..d5360f5d54 --- /dev/null +++ b/layout/generic/MiddleCroppingBlockFrame.cpp @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MiddleCroppingBlockFrame.h" +#include "nsTextFrame.h" +#include "nsLayoutUtils.h" +#include "nsTextNode.h" +#include "nsLineLayout.h" +#include "gfxContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/intl/Segmenter.h" +#include "mozilla/ReflowInput.h" +#include "mozilla/ReflowOutput.h" + +namespace mozilla { + +NS_QUERYFRAME_HEAD(MiddleCroppingBlockFrame) + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) + NS_QUERYFRAME_ENTRY(MiddleCroppingBlockFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) + +MiddleCroppingBlockFrame::MiddleCroppingBlockFrame(ComputedStyle* aStyle, + nsPresContext* aPresContext, + ClassID aClassID) + : nsBlockFrame(aStyle, aPresContext, aClassID) {} + +MiddleCroppingBlockFrame::~MiddleCroppingBlockFrame() = default; + +void MiddleCroppingBlockFrame::UpdateDisplayedValue(const nsAString& aValue, + bool aNotify) { + auto* text = mTextNode.get(); + uint32_t oldLength = aNotify ? 0 : text->TextLength(); + text->SetText(aValue, aNotify); + if (!aNotify) { + // We can't notify during Reflow so we need to tell the text frame about the + // text content change we just did. + if (auto* textFrame = static_cast(text->GetPrimaryFrame())) { + textFrame->NotifyNativeAnonymousTextnodeChange(oldLength); + } + if (LinesBegin() != LinesEnd()) { + AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); + LinesBegin()->MarkDirty(); + } + } +} + +void MiddleCroppingBlockFrame::UpdateDisplayedValueToUncroppedValue( + bool aNotify) { + nsAutoString value; + GetUncroppedValue(value); + UpdateDisplayedValue(value, aNotify); +} + +nscoord MiddleCroppingBlockFrame::GetMinISize(gfxContext* aRenderingContext) { + nscoord result; + DISPLAY_MIN_INLINE_SIZE(this, result); + + // Our min inline size is our pref inline size + result = GetPrefISize(aRenderingContext); + return result; +} + +nscoord MiddleCroppingBlockFrame::GetPrefISize(gfxContext* aRenderingContext) { + nscoord result; + DISPLAY_PREF_INLINE_SIZE(this, result); + + // Make sure we measure with the uncropped value. + if (mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) { + UpdateDisplayedValueToUncroppedValue(false); + } + + result = nsBlockFrame::GetPrefISize(aRenderingContext); + return result; +} + +bool MiddleCroppingBlockFrame::CropTextToWidth(gfxContext& aRenderingContext, + nscoord aWidth, + nsString& aText) const { + if (aText.IsEmpty()) { + return false; + } + + RefPtr fm = nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f); + + // see if the text will completely fit in the width given + if (nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, *fm, + aRenderingContext) <= aWidth) { + return false; + } + + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis(); + + // see if the width is even smaller than the ellipsis + fm->SetTextRunRTL(false); + const nscoord ellipsisWidth = + nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget); + if (ellipsisWidth >= aWidth) { + aText = kEllipsis; + return true; + } + + // determine how much of the string will fit in the max width + nscoord totalWidth = ellipsisWidth; + const Span text(aText); + intl::GraphemeClusterBreakIteratorUtf16 leftIter(text); + intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(text); + uint32_t leftPos = 0; + uint32_t rightPos = aText.Length(); + nsAutoString leftString, rightString; + + while (leftPos < rightPos) { + Maybe pos = leftIter.Next(); + Span chars = text.FromTo(leftPos, *pos); + nscoord charWidth = + nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget); + if (totalWidth + charWidth > aWidth) { + break; + } + + leftString.Append(chars); + leftPos = *pos; + totalWidth += charWidth; + + if (leftPos >= rightPos) { + break; + } + + pos = rightIter.Next(); + chars = text.FromTo(*pos, rightPos); + charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget); + if (totalWidth + charWidth > aWidth) { + break; + } + + rightString.Insert(chars, 0); + rightPos = *pos; + totalWidth += charWidth; + } + + aText = leftString + kEllipsis + rightString; + return true; +} + +void MiddleCroppingBlockFrame::Reflow(nsPresContext* aPresContext, + ReflowOutput& aDesiredSize, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) { + // Restore the uncropped value. + nsAutoString value; + GetUncroppedValue(value); + bool done = false; + while (true) { + UpdateDisplayedValue(value, false); // update the text node + AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); + LinesBegin()->MarkDirty(); + nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus); + if (done) { + break; + } + nscoord currentICoord = aReflowInput.mLineLayout + ? aReflowInput.mLineLayout->GetCurrentICoord() + : 0; + const nscoord availSize = aReflowInput.AvailableISize() - currentICoord; + const nscoord sizeToFit = std::min(aReflowInput.ComputedISize(), availSize); + if (LinesBegin()->ISize() > sizeToFit) { + // The value overflows - crop it and reflow again (once). + if (CropTextToWidth(*aReflowInput.mRenderingContext, sizeToFit, value)) { + nsBlockFrame::DidReflow(aPresContext, &aReflowInput); + aStatus.Reset(); + MarkSubtreeDirty(); + AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); + mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN; + mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN; + done = true; + continue; + } + } + break; + } +} + +nsresult MiddleCroppingBlockFrame::CreateAnonymousContent( + nsTArray& aContent) { + auto* doc = PresContext()->Document(); + mTextNode = new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager()); + // Update the displayed text to reflect the current element's value. + UpdateDisplayedValueToUncroppedValue(false); + aContent.AppendElement(mTextNode); + return NS_OK; +} + +void MiddleCroppingBlockFrame::AppendAnonymousContentTo( + nsTArray& aContent, uint32_t aFilter) { + aContent.AppendElement(mTextNode); +} + +void MiddleCroppingBlockFrame::Destroy(DestroyContext& aContext) { + aContext.AddAnonymousContent(mTextNode.forget()); + nsBlockFrame::Destroy(aContext); +} + +} // namespace mozilla diff --git a/layout/generic/MiddleCroppingBlockFrame.h b/layout/generic/MiddleCroppingBlockFrame.h new file mode 100644 index 0000000000..9086b2b0a3 --- /dev/null +++ b/layout/generic/MiddleCroppingBlockFrame.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_MiddleCroppingBlockFrame_h +#define mozilla_MiddleCroppingBlockFrame_h + +#include "nsBlockFrame.h" +#include "nsIAnonymousContentCreator.h" +#include "nsQueryFrame.h" + +namespace mozilla { + +// A block frame that implements a simplistic version of middle-cropping. Note +// that this is not fully l10n aware or complex-writing-system-friendly, so it's +// generally mostly useful for filenames / urls / etc. +class MiddleCroppingBlockFrame : public nsBlockFrame, + public nsIAnonymousContentCreator { + public: + virtual void GetUncroppedValue(nsAString&) = 0; + void UpdateDisplayedValueToUncroppedValue(bool aNotify); + + NS_DECL_QUERYFRAME_TARGET(MiddleCroppingBlockFrame) + NS_DECL_QUERYFRAME + NS_DECL_ABSTRACT_FRAME(MiddleCroppingBlockFrame) + + protected: + MiddleCroppingBlockFrame(ComputedStyle*, nsPresContext*, ClassID); + + ~MiddleCroppingBlockFrame(); + + void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&, + nsReflowStatus&) override; + + nscoord GetMinISize(gfxContext*) override; + nscoord GetPrefISize(gfxContext*) override; + + /** + * Crop aText to fit inside aWidth using the styles of aFrame. + * @return true if aText was modified + */ + bool CropTextToWidth(gfxContext& aRenderingContext, nscoord aWidth, + nsString& aText) const; + + nsresult CreateAnonymousContent(nsTArray&) override; + void AppendAnonymousContentTo(nsTArray&, + uint32_t aFilter) override; + + /** + * Updates the displayed value by using aValue. + */ + void UpdateDisplayedValue(const nsAString& aValue, bool aNotify); + void Destroy(DestroyContext&) override; + + RefPtr mTextNode; +}; + +} // namespace mozilla + +#endif diff --git a/layout/generic/PrintedSheetFrame.cpp b/layout/generic/PrintedSheetFrame.cpp new file mode 100644 index 0000000000..062ce411f8 --- /dev/null +++ b/layout/generic/PrintedSheetFrame.cpp @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/* Rendering object for a printed or print-previewed sheet of paper */ + +#include "mozilla/PrintedSheetFrame.h" + +#include + +#include "mozilla/StaticPrefs_print.h" +#include "nsCSSFrameConstructor.h" +#include "nsPageFrame.h" +#include "nsPageSequenceFrame.h" + +using namespace mozilla; + +PrintedSheetFrame* NS_NewPrintedSheetFrame(PresShell* aPresShell, + ComputedStyle* aStyle) { + return new (aPresShell) + PrintedSheetFrame(aStyle, aPresShell->GetPresContext()); +} + +namespace mozilla { + +NS_QUERYFRAME_HEAD(PrintedSheetFrame) + NS_QUERYFRAME_ENTRY(PrintedSheetFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) + +NS_IMPL_FRAMEARENA_HELPERS(PrintedSheetFrame) + +void PrintedSheetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) { + if (PresContext()->IsScreen()) { + // Draw the background/shadow/etc. of a blank sheet of paper, for + // print-preview. + DisplayBorderBackgroundOutline(aBuilder, aLists); + } + + for (auto* frame : mFrames) { + if (!frame->HasAnyStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE)) { + BuildDisplayListForChild(aBuilder, frame, aLists); + } + } +} + +// If the given page is included in the user's page range, this function +// returns false. Otherwise, it tags the page with the +// NS_PAGE_SKIPPED_BY_CUSTOM_RANGE state bit and returns true. +static bool TagIfSkippedByCustomRange(nsPageFrame* aPageFrame, int32_t aPageNum, + nsSharedPageData* aPD) { + if (!nsIPrintSettings::IsPageSkipped(aPageNum, aPD->mPageRanges)) { + MOZ_ASSERT(!aPageFrame->HasAnyStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE), + "page frames NS_PAGE_SKIPPED_BY_CUSTOM_RANGE state should " + "only be set if we actually want to skip the page"); + return false; + } + + aPageFrame->AddStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE); + return true; +} + +void PrintedSheetFrame::ClaimPageFrameFromPrevInFlow() { + MoveOverflowToChildList(); +} + +void PrintedSheetFrame::Reflow(nsPresContext* aPresContext, + ReflowOutput& aReflowOutput, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) { + MarkInReflow(); + DO_GLOBAL_REFLOW_COUNT("PrintedSheetFrame"); + DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); + MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); + + // If we have a prev-in-flow, take its overflowing content: + MoveOverflowToChildList(); + + const WritingMode wm = aReflowInput.GetWritingMode(); + + // Count the number of pages that are displayed on this sheet (i.e. how many + // child frames we end up laying out, excluding any pages that are skipped + // due to not being in the user's page-range selection). + uint32_t numPagesOnThisSheet = 0; + + // Target for numPagesOnThisSheet. + const uint32_t desiredPagesPerSheet = mPD->PagesPerSheetInfo()->mNumPages; + + if (desiredPagesPerSheet > 1) { + ComputePagesPerSheetGridMetrics( + nsSize(aReflowInput.AvailableISize(), aReflowInput.AvailableBSize())); + } + + // NOTE: I'm intentionally *not* using a range-based 'for' loop here, since + // we potentially mutate the frame list (appending to the end) during the + // list, which is not generally safe with range-based 'for' loops. + for (auto* childFrame = mFrames.FirstChild(); childFrame; + childFrame = childFrame->GetNextSibling()) { + MOZ_ASSERT(childFrame->IsPageFrame(), + "we're only expecting page frames as children"); + auto* pageFrame = static_cast(childFrame); + + // Be sure our child has a pointer to the nsSharedPageData and knows its + // page number: + pageFrame->SetSharedPageData(mPD); + pageFrame->DeterminePageNum(); + + if (!TagIfSkippedByCustomRange(pageFrame, pageFrame->GetPageNum(), mPD)) { + // The page is going to be displayed on this sheet. Tell it its index + // among the displayed pages, so we can use that to compute its "cell" + // when painting. + pageFrame->SetIndexOnSheet(numPagesOnThisSheet); + numPagesOnThisSheet++; + } + + // This is the app-unit size of the page (in physical & logical units). + // Note: The page sizes come from CSS or else from the user selected size; + // pages are never reflowed to fit their sheet - if/when necessary they are + // scaled to fit their sheet. Hence why we get the page's own dimensions to + // use as its "available space"/"container size" here. + const nsSize physPageSize = pageFrame->ComputePageSize(); + const LogicalSize pageSize(wm, physPageSize); + + ReflowInput pageReflowInput(aPresContext, aReflowInput, pageFrame, + pageSize); + + // For layout purposes, we position *all* our nsPageFrame children at our + // origin. Then, if we have multiple pages-per-sheet, we'll shrink & shift + // each one into the right position as a paint-time effect, in + // BuildDisplayList. + LogicalPoint pagePos(wm); + + // Outparams for reflow: + ReflowOutput pageReflowOutput(pageReflowInput); + nsReflowStatus status; + + ReflowChild(pageFrame, aPresContext, pageReflowOutput, pageReflowInput, wm, + pagePos, physPageSize, ReflowChildFlags::Default, status); + + FinishReflowChild(pageFrame, aPresContext, pageReflowOutput, + &pageReflowInput, wm, pagePos, physPageSize, + ReflowChildFlags::Default); + + // Since we don't support incremental reflow in printed documents (see the + // early-return in nsPageSequenceFrame::Reflow), we can assume that this + // was the first time that pageFrame has been reflowed, and so there's no + // way that it could already have a next-in-flow. If it *did* have a + // next-in-flow, we would need to handle it in the 'status' logic below. + NS_ASSERTION(!pageFrame->GetNextInFlow(), "bad child flow list"); + + // Did this page complete the document, or do we need to generate + // another page frame? + if (status.IsFullyComplete()) { + // The page we just reflowed is the final page! Record its page number + // as the number of pages: + mPD->mRawNumPages = pageFrame->GetPageNum(); + } else { + // Create a continuation for our page frame. We add the continuation to + // our child list, and then potentially push it to our overflow list, if + // it really belongs on the next sheet. + nsIFrame* continuingPage = + PresShell()->FrameConstructor()->CreateContinuingFrame(pageFrame, + this); + mFrames.InsertFrame(nullptr, pageFrame, continuingPage); + const bool isContinuingPageSkipped = + TagIfSkippedByCustomRange(static_cast(continuingPage), + pageFrame->GetPageNum() + 1, mPD); + + // If we've already reached the target number of pages for this sheet, + // and this continuation page that we just created is meant to be + // displayed (i.e. it's in the chosen page range), then we need to push it + // to our overflow list so that it'll go onto a subsequent sheet. + // Otherwise we leave it on this sheet. This ensures we *only* generate + // another sheet IFF there's a displayable page that will end up on it. + if (numPagesOnThisSheet >= desiredPagesPerSheet && + !isContinuingPageSkipped) { + PushChildrenToOverflow(continuingPage, pageFrame); + aStatus.SetIncomplete(); + } + } + } + + // This should hold for the first sheet, because our UI should prevent the + // user from creating a 0-length page range; and it should hold for + // subsequent sheets because we should only create an additional sheet when + // we discover a displayable (i.e. non-skipped) page that we need to push + // to that new sheet. + + // XXXdholbert In certain edge cases (e.g. after a page-orientation-flip that + // reduces the page count), it's possible for us to be given a page range + // that is *entirely out-of-bounds* (with "from" & "to" both being larger + // than our actual page-number count). This scenario produces a single + // PrintedSheetFrame with zero displayable pages on it, which is a weird + // state to be in. This is hopefully a scenario that the frontend code can + // detect and recover from (e.g. by clamping the range to our reported + // `rawNumPages`), but it can't do that until *after* we've completed this + // problematic reflow and can reported an up-to-date `rawNumPages` to the + // frontend. So: to give the frontend a chance to intervene and apply some + // correction/clamping to its print-range parameters, we soften this + // assertion *specifically for the first printed sheet*. + if (!GetPrevContinuation()) { + NS_WARNING_ASSERTION(numPagesOnThisSheet > 0, + "Shouldn't create a sheet with no displayable pages " + "on it"); + } else { + MOZ_ASSERT(numPagesOnThisSheet > 0, + "Shouldn't create a sheet with no displayable pages on it"); + } + + MOZ_ASSERT(numPagesOnThisSheet <= desiredPagesPerSheet, + "Shouldn't have more than desired number of displayable pages " + "on this sheet"); + mNumPages = numPagesOnThisSheet; + + // Populate our ReflowOutput outparam -- just use up all the + // available space, for both our desired size & overflow areas. + aReflowOutput.ISize(wm) = aReflowInput.AvailableISize(); + if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) { + aReflowOutput.BSize(wm) = aReflowInput.AvailableBSize(); + } + aReflowOutput.SetOverflowAreasToDesiredBounds(); + + FinishAndStoreOverflow(&aReflowOutput); +} + +nsSize PrintedSheetFrame::PrecomputeSheetSize( + const nsPresContext* aPresContext) { + mPrecomputedSize = aPresContext->GetPageSize(); + if (mPD->mPrintSettings->HasOrthogonalSheetsAndPages()) { + std::swap(mPrecomputedSize.width, mPrecomputedSize.height); + } + return mPrecomputedSize; +} + +void PrintedSheetFrame::ComputePagesPerSheetGridMetrics( + const nsSize& aSheetSize) { + MOZ_ASSERT(mPD->PagesPerSheetInfo()->mNumPages > 1, + "Unnecessary to call this in a regular 1-page-per-sheet scenario; " + "the computed values won't ever be used in that case"); + + // Compute the space available for the pages-per-sheet "page grid" (just + // subtract the sheet's unwriteable margin area): + nsSize availSpaceOnSheet = aSheetSize; + nsMargin uwm = mPD->mPrintSettings->GetIgnoreUnwriteableMargins() + ? nsMargin{} + : nsPresContext::CSSTwipsToAppUnits( + mPD->mPrintSettings->GetUnwriteableMarginInTwips()); + + // XXXjwatt Once we support heterogeneous sheet orientations, we'll also need + // to rotate uwm if this sheet is not the primary orientation. + if (mPD->mPrintSettings->HasOrthogonalSheetsAndPages()) { + // aSheetSize already takes account of the switch of *sheet* orientation + // that we do in this case (the orientation implied by the page size + // dimensions in the nsIPrintSettings applies to *pages*). That is not the + // case for the unwriteable margins since we got them from the + // nsIPrintSettings object ourself, so we need to adjust `uwm` here. + // + // Note: In practice, sheets with an orientation that is orthogonal to the + // physical orientation of sheets output by a printer must be rotated 90 + // degrees for/by the printer. In that case the convention seems to be that + // the "left" edge of the orthogonally oriented sheet becomes the "top", + // and so forth. The rotation direction will matter in the case that the + // top and bottom unwriteable margins are different, or the left and right + // unwriteable margins are different. So we need to match this behavior, + // which means we must rotate the `uwm` 90 degrees *counter-clockwise*. + nsMargin rotated(uwm.right, uwm.bottom, uwm.left, uwm.top); + uwm = rotated; + } + + availSpaceOnSheet.width -= uwm.LeftRight(); + availSpaceOnSheet.height -= uwm.TopBottom(); + + if (MOZ_UNLIKELY(availSpaceOnSheet.IsEmpty())) { + // This sort of thing should be rare, but it can happen if there are + // bizarre page sizes, and/or if there's an unexpectedly large unwriteable + // margin area. + NS_WARNING("Zero area for pages-per-sheet grid, or zero-sized grid"); + mGridOrigin = nsPoint(0, 0); + mGridNumCols = 1; + return; + } + + // If there are a different number of rows vs. cols, we'll aim to put + // the larger number of items in the longer axis. + const auto* ppsInfo = mPD->PagesPerSheetInfo(); + uint32_t smallerNumTracks = ppsInfo->mNumPages / ppsInfo->mLargerNumTracks; + bool sheetIsPortraitLike = aSheetSize.width < aSheetSize.height; + auto numCols = + sheetIsPortraitLike ? smallerNumTracks : ppsInfo->mLargerNumTracks; + auto numRows = + sheetIsPortraitLike ? ppsInfo->mLargerNumTracks : smallerNumTracks; + + mGridOrigin = nsPoint(uwm.left, uwm.top); + mGridNumCols = numCols; + mGridCellWidth = availSpaceOnSheet.width / nscoord(numCols); + mGridCellHeight = availSpaceOnSheet.height / nscoord(numRows); +} + +#ifdef DEBUG_FRAME_DUMP +nsresult PrintedSheetFrame::GetFrameName(nsAString& aResult) const { + return MakeFrameName(u"PrintedSheet"_ns, aResult); +} +#endif + +} // namespace mozilla diff --git a/layout/generic/PrintedSheetFrame.h b/layout/generic/PrintedSheetFrame.h new file mode 100644 index 0000000000..183e16fc4e --- /dev/null +++ b/layout/generic/PrintedSheetFrame.h @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/* Rendering object for a printed or print-previewed sheet of paper */ + +#ifndef LAYOUT_GENERIC_PRINTEDSHEETFRAME_H_ +#define LAYOUT_GENERIC_PRINTEDSHEETFRAME_H_ + +#include "nsContainerFrame.h" +#include "nsHTMLParts.h" + +class nsSharedPageData; + +namespace mozilla { + +class PrintedSheetFrame final : public nsContainerFrame { + public: + NS_DECL_QUERYFRAME + NS_DECL_FRAMEARENA_HELPERS(PrintedSheetFrame) + + friend PrintedSheetFrame* ::NS_NewPrintedSheetFrame( + mozilla::PresShell* aPresShell, ComputedStyle* aStyle); + + void SetSharedPageData(nsSharedPageData* aPD) { mPD = aPD; } + + // Invokes MoveOverflowToChildList. + // This is intended for use by callers that need to be able to get our first/ + // only nsPageFrame from our child list to examine its computed style just + // **prior** to us being reflowed. (If our first nsPageFrame will come from + // our prev-in-flow, we won't otherwise take ownership of it until we are + // reflowed.) + void ClaimPageFrameFromPrevInFlow(); + + // nsIFrame overrides + void Reflow(nsPresContext* aPresContext, ReflowOutput& aReflowOutput, + const ReflowInput& aReflowInput, + nsReflowStatus& aStatus) override; + + void BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) override; + +#ifdef DEBUG_FRAME_DUMP + nsresult GetFrameName(nsAString& aResult) const override; +#endif + + uint32_t GetNumPages() const { return mNumPages; } + + // These methods provide information about the grid that pages should be + // placed into in the case that there are multiple pages-per-sheet. + uint32_t GetGridNumCols() const { return mGridNumCols; } + nsPoint GetGridOrigin() const { return mGridOrigin; } + nscoord GetGridCellWidth() const { return mGridCellWidth; } + nscoord GetGridCellHeight() const { return mGridCellHeight; } + + /** + * A helper that is called just prior to this frame being relfowed to + * pre-compute and cache the size that the sheet should be given. This is + * called before any child nsPageFrames are reflowed, and it is cached so + * that those nsPageFrames can obtain their sheet frame's size while they're + * reflowing (the normal reflow code doesn't give the sheet frame its size + * until after the nsPageFrames have been reflowed). + * If we get rid of nsPageFrame::ComputeSinglePPSPageSizeScale (bug 1835782), + * which is the only consumer of GetPrecomputedSheetSize, then we can get rid + * of GetPrecomputedSheetSize and the member variable and rename + * PrecomputeSheetSize to ComputeSheetSize, which will then only be called + * once during reflow. + */ + nsSize PrecomputeSheetSize(const nsPresContext* aPresContext); + nsSize GetPrecomputedSheetSize() const { return mPrecomputedSize; } + + private: + // Private construtor & destructor, to avoid accidental (non-FrameArena) + // instantiation/deletion: + PrintedSheetFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) + : nsContainerFrame(aStyle, aPresContext, kClassID) {} + ~PrintedSheetFrame() = default; + + // Helper function to populate some pages-per-sheet metrics in our + // nsSharedPageData. + // XXXjwatt: We should investigate sharing this function for the single + // page-per-sheet case (bug 1835782). The logic for that case + // (nsPageFrame::ComputePageSizeScale) is somewhat different though, since + // that case uses no sheet margins and uses the user/CSS specified margins on + // the page, with any page scaling reverted to keep the margins unchanged. + // We, on the other hand, use the unwriteable margins for the sheet, unscaled, + // and use the user/CSS margins on the pages and allow them to be scaled + // along with any pages-per-sheet scaling. (This behavior makes maximum use + // of the sheet and, by scaling the default on the pages, results in a + // a sensible amount of spacing between pages.) + void ComputePagesPerSheetGridMetrics(const nsSize& aSheetSize); + + nsSize mPrecomputedSize; + + // Note: this will be set before reflow, and it's strongly owned by our + // nsPageSequenceFrame, which outlives us. + nsSharedPageData* mPD = nullptr; + + // The number of visible pages in this sheet. + uint32_t mNumPages = 0; + + // Number of "columns" in our pages-per-sheet layout. For example: if we're + // printing with 6 pages-per-sheet, then this could be either 3 or 2, + // depending on whether we're printing portrait-oriented pages onto a + // landscape-oriented sheet (3 cols) vs. if we're printing landscape-oriented + // pages onto a portrait-oriented sheet (2 cols). + uint32_t mGridNumCols = 1; + + // The offset of the start of the multiple pages-per-sheet grid from the + // top-left of the sheet. + nsPoint mGridOrigin; + + // The size of each cell on the sheet into which pages are to be placed. + // (The default values are arbitrary.) + nscoord mGridCellWidth = 1; + nscoord mGridCellHeight = 1; +}; + +} // namespace mozilla + +#endif /* LAYOUT_GENERIC_PRINTEDSHEETFRAME_H_ */ diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp new file mode 100644 index 0000000000..c8b01fff19 --- /dev/null +++ b/layout/generic/ReflowInput.cpp @@ -0,0 +1,3045 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* struct containing the input to nsIFrame::Reflow */ + +#include "mozilla/ReflowInput.h" + +#include + +#include "CounterStyleManager.h" +#include "LayoutLogging.h" +#include "mozilla/dom/HTMLInputElement.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/WritingModes.h" +#include "nsBlockFrame.h" +#include "nsCSSAnonBoxes.h" +#include "nsFlexContainerFrame.h" +#include "nsFontInflationData.h" +#include "nsFontMetrics.h" +#include "nsGkAtoms.h" +#include "nsGridContainerFrame.h" +#include "nsIContent.h" +#include "nsIFrame.h" +#include "nsIFrameInlines.h" +#include "nsImageFrame.h" +#include "nsIPercentBSizeObserver.h" +#include "nsLayoutUtils.h" +#include "nsLineBox.h" +#include "nsPresContext.h" +#include "nsStyleConsts.h" +#include "nsTableCellFrame.h" +#include "nsTableFrame.h" +#include "StickyScrollContainer.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; +using namespace mozilla::layout; + +static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) { + nsIFrame* frameNext = aFrame->GetNextInFlow(); + nsIFrame* parentNext = aParent->GetNextInFlow(); + return frameNext && parentNext && frameNext->GetParent() == parentNext; +} + +/** + * Adjusts the margin for a list (ol, ul), if necessary, depending on + * font inflation settings. Unfortunately, because bullets from a list are + * placed in the margin area, we only have ~40px in which to place the + * bullets. When they are inflated, however, this causes problems, since + * the text takes up more space than is available in the margin. + * + * This method will return a small amount (in app units) by which the + * margin can be adjusted, so that the space is available for list + * bullets to be rendered with font inflation enabled. + */ +static nscoord FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) { + if (!aFrame->IsBlockFrameOrSubclass()) { + return 0; + } + + // We only want to adjust the margins if we're dealing with an ordered list. + const nsBlockFrame* blockFrame = static_cast(aFrame); + if (!blockFrame->HasMarker()) { + return 0; + } + + float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); + if (inflation <= 1.0f) { + return 0; + } + + // The HTML spec states that the default padding for ordered lists + // begins at 40px, indicating that we have 40px of space to place a + // bullet. When performing font inflation calculations, we add space + // equivalent to this, but simply inflated at the same amount as the + // text, in app units. + auto margin = nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); + + auto* list = aFrame->StyleList(); + if (!list->mCounterStyle.IsAtom()) { + return margin; + } + + nsAtom* type = list->mCounterStyle.AsAtom(); + if (type != nsGkAtoms::none && type != nsGkAtoms::disc && + type != nsGkAtoms::circle && type != nsGkAtoms::square && + type != nsGkAtoms::disclosure_closed && + type != nsGkAtoms::disclosure_open) { + return margin; + } + + return 0; +} + +SizeComputationInput::SizeComputationInput(nsIFrame* aFrame, + gfxContext* aRenderingContext) + : mFrame(aFrame), + mRenderingContext(aRenderingContext), + mWritingMode(aFrame->GetWritingMode()), + mIsThemed(aFrame->IsThemed()), + mComputedMargin(mWritingMode), + mComputedBorderPadding(mWritingMode), + mComputedPadding(mWritingMode) { + MOZ_ASSERT(mFrame); +} + +SizeComputationInput::SizeComputationInput( + nsIFrame* aFrame, gfxContext* aRenderingContext, + WritingMode aContainingBlockWritingMode, nscoord aContainingBlockISize, + const Maybe& aBorder, const Maybe& aPadding) + : SizeComputationInput(aFrame, aRenderingContext) { + MOZ_ASSERT(!mFrame->IsTableColFrame()); + InitOffsets(aContainingBlockWritingMode, aContainingBlockISize, + mFrame->Type(), {}, aBorder, aPadding); +} + +// Initialize a root reflow input with a rendering context to +// use for measuring things. +ReflowInput::ReflowInput(nsPresContext* aPresContext, nsIFrame* aFrame, + gfxContext* aRenderingContext, + const LogicalSize& aAvailableSpace, InitFlags aFlags) + : SizeComputationInput(aFrame, aRenderingContext), + mAvailableSize(aAvailableSpace) { + MOZ_ASSERT(aRenderingContext, "no rendering context"); + MOZ_ASSERT(aPresContext, "no pres context"); + MOZ_ASSERT(aFrame, "no frame"); + MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); + + if (aFlags.contains(InitFlag::DummyParentReflowInput)) { + mFlags.mDummyParentReflowInput = true; + } + if (aFlags.contains(InitFlag::StaticPosIsCBOrigin)) { + mFlags.mStaticPosIsCBOrigin = true; + } + + if (!aFlags.contains(InitFlag::CallerWillInit)) { + Init(aPresContext); + } + // When we encounter a PageContent frame this will be set to true. + mFlags.mCanHaveClassABreakpoints = false; +} + +// Initialize a reflow input for a child frame's reflow. Some state +// is copied from the parent reflow input; the remaining state is +// computed. +ReflowInput::ReflowInput(nsPresContext* aPresContext, + const ReflowInput& aParentReflowInput, + nsIFrame* aFrame, const LogicalSize& aAvailableSpace, + const Maybe& aContainingBlockSize, + InitFlags aFlags, + const StyleSizeOverrides& aSizeOverrides, + ComputeSizeFlags aComputeSizeFlags) + : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext), + mParentReflowInput(&aParentReflowInput), + mFloatManager(aParentReflowInput.mFloatManager), + mLineLayout(mFrame->IsFrameOfType(nsIFrame::eLineParticipant) + ? aParentReflowInput.mLineLayout + : nullptr), + mBreakType(aParentReflowInput.mBreakType), + mPercentBSizeObserver( + (aParentReflowInput.mPercentBSizeObserver && + aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this)) + ? aParentReflowInput.mPercentBSizeObserver + : nullptr), + mFlags(aParentReflowInput.mFlags), + mStyleSizeOverrides(aSizeOverrides), + mComputeSizeFlags(aComputeSizeFlags), + mReflowDepth(aParentReflowInput.mReflowDepth + 1), + mAvailableSize(aAvailableSpace) { + MOZ_ASSERT(aPresContext, "no pres context"); + MOZ_ASSERT(aFrame, "no frame"); + MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); + MOZ_ASSERT(!mFlags.mSpecialBSizeReflow || !aFrame->IsSubtreeDirty(), + "frame should be clean when getting special bsize reflow"); + + if (mWritingMode.IsOrthogonalTo(aParentReflowInput.GetWritingMode())) { + // If we're setting up for an orthogonal flow, and the parent reflow input + // had a constrained ComputedBSize, we can use that as our AvailableISize + // in preference to leaving it unconstrained. + if (AvailableISize() == NS_UNCONSTRAINEDSIZE && + aParentReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { + SetAvailableISize(aParentReflowInput.ComputedBSize()); + } + } + + // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in + // this constructor's init list, so the only flags that we need to explicitly + // initialize here are those that may need a value other than our parent's. + mFlags.mNextInFlowUntouched = + aParentReflowInput.mFlags.mNextInFlowUntouched && + CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame); + mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; + mFlags.mIsColumnBalancing = false; + mFlags.mColumnSetWrapperHasNoBSizeLeft = false; + mFlags.mTreatBSizeAsIndefinite = false; + mFlags.mDummyParentReflowInput = false; + mFlags.mStaticPosIsCBOrigin = aFlags.contains(InitFlag::StaticPosIsCBOrigin); + mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false; + + // aPresContext->IsPaginated() and the named pages pref should have been + // checked when constructing the root ReflowInput. + if (aParentReflowInput.mFlags.mCanHaveClassABreakpoints) { + MOZ_ASSERT(aPresContext->IsPaginated(), + "mCanHaveClassABreakpoints set during non-paginated reflow."); + } + + { + using mozilla::LayoutFrameType; + switch (mFrame->Type()) { + case LayoutFrameType::PageContent: + // PageContent requires paginated reflow. + MOZ_ASSERT(aPresContext->IsPaginated(), + "nsPageContentFrame should not be in non-paginated reflow"); + MOZ_ASSERT(!mFlags.mCanHaveClassABreakpoints, + "mFlags.mCanHaveClassABreakpoints should have been " + "initalized to false before we found nsPageContentFrame"); + mFlags.mCanHaveClassABreakpoints = true; + break; + case LayoutFrameType::Block: // FALLTHROUGH + case LayoutFrameType::Canvas: // FALLTHROUGH + case LayoutFrameType::FlexContainer: // FALLTHROUGH + case LayoutFrameType::GridContainer: + if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { + // Never allow breakpoints inside of out-of-flow frames. + mFlags.mCanHaveClassABreakpoints = false; + break; + } + // This frame type can have class A breakpoints, inherit this flag + // from the parent (this is done for all flags during construction). + // This also includes Canvas frames, as each PageContent frame always + // has exactly one child which is a Canvas frame. + // Do NOT include the subclasses of BlockFrame here, as the ones for + // which this could be applicable (ColumnSetWrapper and the MathML + // frames) cannot have class A breakpoints. + MOZ_ASSERT(mFlags.mCanHaveClassABreakpoints == + aParentReflowInput.mFlags.mCanHaveClassABreakpoints); + break; + default: + mFlags.mCanHaveClassABreakpoints = false; + break; + } + } + + if (aFlags.contains(InitFlag::DummyParentReflowInput) || + (mParentReflowInput->mFlags.mDummyParentReflowInput && + mFrame->IsTableFrame())) { + mFlags.mDummyParentReflowInput = true; + } + + if (!aFlags.contains(InitFlag::CallerWillInit)) { + Init(aPresContext, aContainingBlockSize); + } +} + +template +inline nscoord SizeComputationInput::ComputeISizeValue( + const WritingMode aWM, const LogicalSize& aContainingBlockSize, + const LogicalSize& aContentEdgeToBoxSizing, nscoord aBoxSizingToMarginEdge, + const SizeOrMaxSize& aSize) const { + return mFrame + ->ComputeISizeValue(mRenderingContext, aWM, aContainingBlockSize, + aContentEdgeToBoxSizing, aBoxSizingToMarginEdge, + aSize) + .mISize; +} + +template +nscoord SizeComputationInput::ComputeISizeValue( + const LogicalSize& aContainingBlockSize, StyleBoxSizing aBoxSizing, + const SizeOrMaxSize& aSize) const { + WritingMode wm = GetWritingMode(); + const auto borderPadding = ComputedLogicalBorderPadding(wm); + LogicalSize inside = aBoxSizing == StyleBoxSizing::Border + ? borderPadding.Size(wm) + : LogicalSize(wm); + nscoord outside = + borderPadding.IStartEnd(wm) + ComputedLogicalMargin(wm).IStartEnd(wm); + outside -= inside.ISize(wm); + + return ComputeISizeValue(wm, aContainingBlockSize, inside, outside, aSize); +} + +nscoord SizeComputationInput::ComputeBSizeValue( + nscoord aContainingBlockBSize, StyleBoxSizing aBoxSizing, + const LengthPercentage& aSize) const { + WritingMode wm = GetWritingMode(); + nscoord inside = 0; + if (aBoxSizing == StyleBoxSizing::Border) { + inside = ComputedLogicalBorderPadding(wm).BStartEnd(wm); + } + return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, inside, aSize); +} + +bool ReflowInput::ShouldReflowAllKids() const { + // Note that we could make a stronger optimization for IsBResize if + // we use it in a ShouldReflowChild test that replaces the current + // checks of NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN, if it + // were tested there along with NS_FRAME_CONTAINS_RELATIVE_BSIZE. + // This would need to be combined with a slight change in which + // frames NS_FRAME_CONTAINS_RELATIVE_BSIZE is marked on. + return mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsIResize() || + (IsBResize() && + mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) || + mFlags.mIsInLastColumnBalancingReflow; +} + +void ReflowInput::SetComputedISize(nscoord aComputedISize, + ResetResizeFlags aFlags) { + // It'd be nice to assert that |frame| is not in reflow, but this fails + // because viewport frames reset the computed isize on a copy of their reflow + // input when reflowing fixed-pos kids. In that case we actually don't want + // to mess with the resize flags, because comparing the frame's rect to the + // munged computed isize is pointless. + NS_WARNING_ASSERTION(aComputedISize >= 0, "Invalid computed inline-size!"); + if (ComputedISize() != aComputedISize) { + mComputedSize.ISize(mWritingMode) = std::max(0, aComputedISize); + if (aFlags == ResetResizeFlags::Yes) { + InitResizeFlags(mFrame->PresContext(), mFrame->Type()); + } + } +} + +void ReflowInput::SetComputedBSize(nscoord aComputedBSize, + ResetResizeFlags aFlags) { + // It'd be nice to assert that |frame| is not in reflow, but this fails + // for the same reason as above. + NS_WARNING_ASSERTION(aComputedBSize >= 0, "Invalid computed block-size!"); + if (ComputedBSize() != aComputedBSize) { + mComputedSize.BSize(mWritingMode) = std::max(0, aComputedBSize); + InitResizeFlags(mFrame->PresContext(), mFrame->Type()); + } +} + +void ReflowInput::Init(nsPresContext* aPresContext, + const Maybe& aContainingBlockSize, + const Maybe& aBorder, + const Maybe& aPadding) { + if (AvailableISize() == NS_UNCONSTRAINEDSIZE) { + // Look up the parent chain for an orthogonal inline limit, + // and reset AvailableISize() if found. + for (const ReflowInput* parent = mParentReflowInput; parent != nullptr; + parent = parent->mParentReflowInput) { + if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) && + parent->mOrthogonalLimit != NS_UNCONSTRAINEDSIZE) { + SetAvailableISize(parent->mOrthogonalLimit); + break; + } + } + } + + LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE, + "have unconstrained inline-size; this should only " + "result from very large sizes, not attempts at " + "intrinsic inline-size calculation"); + + mStylePosition = mFrame->StylePosition(); + mStyleDisplay = mFrame->StyleDisplay(); + mStyleBorder = mFrame->StyleBorder(); + mStyleMargin = mFrame->StyleMargin(); + + InitCBReflowInput(); + + LayoutFrameType type = mFrame->Type(); + if (type == mozilla::LayoutFrameType::Placeholder) { + // Placeholders have a no-op Reflow method that doesn't need the rest of + // this initialization, so we bail out early. + mComputedSize.SizeTo(mWritingMode, 0, 0); + return; + } + + mFlags.mIsReplaced = mFrame->IsFrameOfType(nsIFrame::eReplaced) || + mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock); + + InitConstraints(aPresContext, aContainingBlockSize, aBorder, aPadding, type); + + InitResizeFlags(aPresContext, type); + InitDynamicReflowRoot(); + + nsIFrame* parent = mFrame->GetParent(); + if (parent && parent->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE) && + !(parent->IsScrollFrame() && + parent->StyleDisplay()->mOverflowY != StyleOverflow::Hidden)) { + mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + } else if (type == LayoutFrameType::SVGForeignObject) { + // An SVG foreignObject frame is inherently constrained block-size. + mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + } else { + const auto& bSizeCoord = mStylePosition->BSize(mWritingMode); + const auto& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode); + if ((!bSizeCoord.BehavesLikeInitialValueOnBlockAxis() || + !maxBSizeCoord.BehavesLikeInitialValueOnBlockAxis()) && + // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements. + (mFrame->GetContent() && !(mFrame->GetContent()->IsAnyOfHTMLElements( + nsGkAtoms::body, nsGkAtoms::html)))) { + // If our block-size was specified as a percentage, then this could + // actually resolve to 'auto', based on: + // http://www.w3.org/TR/CSS21/visudet.html#the-height-property + nsIFrame* containingBlk = mFrame; + while (containingBlk) { + const nsStylePosition* stylePos = containingBlk->StylePosition(); + const auto& bSizeCoord = stylePos->BSize(mWritingMode); + const auto& maxBSizeCoord = stylePos->MaxBSize(mWritingMode); + if ((bSizeCoord.IsLengthPercentage() && !bSizeCoord.HasPercent()) || + (maxBSizeCoord.IsLengthPercentage() && + !maxBSizeCoord.HasPercent())) { + mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + break; + } else if (bSizeCoord.HasPercent() || maxBSizeCoord.HasPercent()) { + if (!(containingBlk = containingBlk->GetContainingBlock())) { + // If we've reached the top of the tree, then we don't have + // a constrained block-size. + mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + break; + } + + continue; + } else { + mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + break; + } + } + } else { + mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); + } + } + + if (mParentReflowInput && + mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { + // Orthogonal frames are always reflowed with an unconstrained + // dimension to avoid incomplete reflow across an orthogonal + // boundary. Normally this is the block-size, but for column sets + // with auto-height it's the inline-size, so that they can add + // columns in the container's block direction + if (type == LayoutFrameType::ColumnSet && + mStylePosition->ISize(mWritingMode).IsAuto()) { + SetComputedISize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No); + } else { + SetAvailableBSize(NS_UNCONSTRAINEDSIZE); + } + } + + if (mFrame->GetContainSizeAxes().mBContained) { + // In the case that a box is size contained in block axis, we want to ensure + // that it is also monolithic. We do this by setting AvailableBSize() to an + // unconstrained size to avoid fragmentation. + SetAvailableBSize(NS_UNCONSTRAINEDSIZE); + } + + LAYOUT_WARN_IF_FALSE((mStyleDisplay->IsInlineOutsideStyle() && + !mFrame->IsFrameOfType(nsIFrame::eReplaced)) || + type == LayoutFrameType::Text || + ComputedISize() != NS_UNCONSTRAINEDSIZE, + "have unconstrained inline-size; this should only " + "result from very large sizes, not attempts at " + "intrinsic inline-size calculation"); +} + +static bool MightBeContainingBlockFor(nsIFrame* aMaybeContainingBlock, + nsIFrame* aFrame, + const nsStyleDisplay* aStyleDisplay) { + // Keep this in sync with nsIFrame::GetContainingBlock. + if (aFrame->IsAbsolutelyPositioned(aStyleDisplay) && + aMaybeContainingBlock == aFrame->GetParent()) { + return true; + } + return aMaybeContainingBlock->IsBlockContainer(); +} + +void ReflowInput::InitCBReflowInput() { + if (!mParentReflowInput) { + mCBReflowInput = nullptr; + return; + } + if (mParentReflowInput->mFlags.mDummyParentReflowInput) { + mCBReflowInput = mParentReflowInput; + return; + } + + // To avoid a long walk up the frame tree check if the parent frame can be a + // containing block for mFrame. + if (MightBeContainingBlockFor(mParentReflowInput->mFrame, mFrame, + mStyleDisplay) && + mParentReflowInput->mFrame == + mFrame->GetContainingBlock(0, mStyleDisplay)) { + // Inner table frames need to use the containing block of the outer + // table frame. + if (mFrame->IsTableFrame()) { + mCBReflowInput = mParentReflowInput->mCBReflowInput; + } else { + mCBReflowInput = mParentReflowInput; + } + } else { + mCBReflowInput = mParentReflowInput->mCBReflowInput; + } +} + +/* Check whether CalcQuirkContainingBlockHeight would stop on the + * given reflow input, using its block as a height. (essentially + * returns false for any case in which CalcQuirkContainingBlockHeight + * has a "continue" in its main loop.) + * + * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses + * this function as well + */ +static bool IsQuirkContainingBlockHeight(const ReflowInput* rs, + LayoutFrameType aFrameType) { + if (LayoutFrameType::Block == aFrameType || + LayoutFrameType::Scroll == aFrameType) { + // Note: This next condition could change due to a style change, + // but that would cause a style reflow anyway, which means we're ok. + if (NS_UNCONSTRAINEDSIZE == rs->ComputedHeight()) { + if (!rs->mFrame->IsAbsolutelyPositioned(rs->mStyleDisplay)) { + return false; + } + } + } + return true; +} + +void ReflowInput::InitResizeFlags(nsPresContext* aPresContext, + LayoutFrameType aFrameType) { + SetBResize(false); + SetIResize(false); + mFlags.mIsBResizeForPercentages = false; + + const WritingMode wm = mWritingMode; // just a shorthand + // We should report that we have a resize in the inline dimension if + // *either* the border-box size or the content-box size in that + // dimension has changed. It might not actually be necessary to do + // this if the border-box size has changed and the content-box size + // has not changed, but since we've historically used the flag to mean + // border-box size change, continue to do that. It's possible for + // the content-box size to change without a border-box size change or + // a style change given (1) a fixed width (possibly fixed by max-width + // or min-width), box-sizing:border-box, and percentage padding; + // (2) box-sizing:content-box, M% width, and calc(Npx - M%) padding. + // + // However, we don't actually have the information at this point to tell + // whether the content-box size has changed, since both style data and the + // UsedPaddingProperty() have already been updated in + // SizeComputationInput::InitOffsets(). So, we check the HasPaddingChange() + // bit for the cases where it's possible for the content-box size to have + // changed without either (a) a change in the border-box size or (b) an + // nsChangeHint_NeedDirtyReflow change hint due to change in border or + // padding. + // + // We don't clear the HasPaddingChange() bit here, since sometimes we + // construct reflow input (e.g. in nsBlockFrame::ReflowBlockFrame to compute + // margin collapsing) without reflowing the frame. Instead, we clear it in + // nsIFrame::DidReflow(). + bool isIResize = + // is the border-box resizing? + mFrame->ISize(wm) != + ComputedISize() + ComputedLogicalBorderPadding(wm).IStartEnd(wm) || + // or is the content-box resizing? (see comment above) + mFrame->HasPaddingChange(); + + if (mFrame->HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT) && + nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { + // Create our font inflation data if we don't have it already, and + // give it our current width information. + bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) && + // Avoid running this at the box-to-block interface + // (where we shouldn't be inflating anyway, and where + // reflow input construction is probably to construct a + // dummy parent reflow input anyway). + !mFlags.mDummyParentReflowInput; + + if (dirty || (!mFrame->GetParent() && isIResize)) { + // When font size inflation is enabled, a change in either: + // * the effective width of a font inflation flow root + // * the width of the frame + // needs to cause a dirty reflow since they change the font size + // inflation calculations, which in turn change the size of text, + // line-heights, etc. This is relatively similar to a classic + // case of style change reflow, except that because inflation + // doesn't affect the intrinsic sizing codepath, there's no need + // to invalidate intrinsic sizes. + // + // Note that this makes horizontal resizing a good bit more + // expensive. However, font size inflation is targeted at a set of + // devices (zoom-and-pan devices) where the main use case for + // horizontal resizing needing to be efficient (window resizing) is + // not present. It does still increase the cost of dynamic changes + // caused by script where a style or content change in one place + // causes a resize in another (e.g., rebalancing a table). + + // FIXME: This isn't so great for the cases where + // ReflowInput::SetComputedWidth is called, if the first time + // we go through InitResizeFlags we set IsHResize() to true, and then + // the second time we'd set it to false even without the + // NS_FRAME_IS_DIRTY bit already set. + if (mFrame->IsSVGForeignObjectFrame()) { + // Foreign object frames use dirty bits in a special way. + mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); + nsIFrame* kid = mFrame->PrincipalChildList().FirstChild(); + if (kid) { + kid->MarkSubtreeDirty(); + } + } else { + mFrame->MarkSubtreeDirty(); + } + + // Mark intrinsic widths on all descendants dirty. We need to do + // this (1) since we're changing the size of text and need to + // clear text runs on text frames and (2) since we actually are + // changing some intrinsic widths, but only those that live inside + // of containers. + + // It makes sense to do this for descendants but not ancestors + // (which is unusual) because we're only changing the unusual + // inflation-dependent intrinsic widths (i.e., ones computed with + // nsPresContext::mInflationDisabledForShrinkWrap set to false), + // which should never affect anything outside of their inflation + // flow root (or, for that matter, even their inflation + // container). + + // This is also different from what PresShell::FrameNeedsReflow + // does because it doesn't go through placeholders. It doesn't + // need to because we're actually doing something that cares about + // frame tree geometry (the width on an ancestor) rather than + // style. + + AutoTArray stack; + stack.AppendElement(mFrame); + + do { + nsIFrame* f = stack.PopLastElement(); + for (const auto& childList : f->ChildLists()) { + for (nsIFrame* kid : childList.mList) { + kid->MarkIntrinsicISizesDirty(); + stack.AppendElement(kid); + } + } + } while (stack.Length() != 0); + } + } + + SetIResize(!mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY) && isIResize); + + // XXX Should we really need to null check mCBReflowInput? (We do for + // at least nsBoxFrame). + if (mFrame->HasBSizeChange()) { + // When we have an nsChangeHint_UpdateComputedBSize, we'll set a bit + // on the frame to indicate we're resizing. This might catch cases, + // such as a change between auto and a length, where the box doesn't + // actually resize but children with percentages resize (since those + // percentages become auto if their containing block is auto). + SetBResize(true); + mFlags.mIsBResizeForPercentages = true; + // We don't clear the HasBSizeChange state here, since sometimes we + // construct a ReflowInput (e.g. in nsBlockFrame::ReflowBlockFrame to + // compute margin collapsing) without reflowing the frame. Instead, we + // clear it in nsIFrame::DidReflow. + } else if (mCBReflowInput && + mCBReflowInput->IsBResizeForPercentagesForWM(wm) && + (mStylePosition->BSize(wm).HasPercent() || + mStylePosition->MinBSize(wm).HasPercent() || + mStylePosition->MaxBSize(wm).HasPercent())) { + // We have a percentage (or calc-with-percentage) block-size, and the + // value it's relative to has changed. + SetBResize(true); + mFlags.mIsBResizeForPercentages = true; + } else if (aFrameType == LayoutFrameType::TableCell && + (mFlags.mSpecialBSizeReflow || + mFrame->FirstInFlow()->HasAnyStateBits( + NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && + mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { + // Need to set the bit on the cell so that + // mCBReflowInput->IsBResize() is set correctly below when + // reflowing descendant. + SetBResize(true); + mFlags.mIsBResizeForPercentages = true; + } else if (mCBReflowInput && mFrame->IsBlockWrapper()) { + // XXX Is this problematic for relatively positioned inlines acting + // as containing block for absolutely positioned elements? + // Possibly; in that case we should at least be checking + // IsSubtreeDirty(), I'd think. + SetBResize(mCBReflowInput->IsBResizeForWM(wm)); + mFlags.mIsBResizeForPercentages = + mCBReflowInput->IsBResizeForPercentagesForWM(wm); + } else if (ComputedBSize() == NS_UNCONSTRAINEDSIZE) { + // We have an 'auto' block-size. + if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && + mCBReflowInput) { + // FIXME: This should probably also check IsIResize(). + SetBResize(mCBReflowInput->IsBResizeForWM(wm)); + } else { + SetBResize(IsIResize()); + } + SetBResize(IsBResize() || mFrame->IsSubtreeDirty()); + } else { + // We have a non-'auto' block-size, i.e., a length. Set the BResize + // flag to whether the size is actually different. + SetBResize(mFrame->BSize(wm) != + ComputedBSize() + + ComputedLogicalBorderPadding(wm).BStartEnd(wm)); + } + + bool dependsOnCBBSize = (mStylePosition->BSizeDependsOnContainer(wm) && + // FIXME: condition this on not-abspos? + !mStylePosition->BSize(wm).IsAuto()) || + mStylePosition->MinBSizeDependsOnContainer(wm) || + mStylePosition->MaxBSizeDependsOnContainer(wm) || + mStylePosition->mOffset.GetBStart(wm).HasPercent() || + !mStylePosition->mOffset.GetBEnd(wm).IsAuto(); + + // If mFrame is a flex item, and mFrame's block axis is the flex container's + // main axis (e.g. in a column-oriented flex container with same + // writing-mode), then its block-size depends on its CB size, if its + // flex-basis has a percentage. + if (mFrame->IsFlexItem() && + !nsFlexContainerFrame::IsItemInlineAxisMainAxis(mFrame)) { + const auto& flexBasis = mStylePosition->mFlexBasis; + dependsOnCBBSize |= (flexBasis.IsSize() && flexBasis.AsSize().HasPercent()); + } + + if (mFrame->StyleText()->mLineHeight.IsMozBlockHeight()) { + // line-height depends on block bsize + mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); + // but only on containing blocks if this frame is not a suitable block + dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame); + } + + // If we're the descendant of a table cell that performs special bsize + // reflows and we could be the child that requires them, always set + // the block-axis resize in case this is the first pass before the + // special bsize reflow. However, don't do this if it actually is + // the special bsize reflow, since in that case it will already be + // set correctly above if we need it set. + if (!IsBResize() && mCBReflowInput && + (mCBReflowInput->mFrame->IsTableCellFrame() || + mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) && + !mCBReflowInput->mFlags.mSpecialBSizeReflow && dependsOnCBBSize) { + SetBResize(true); + mFlags.mHeightDependsOnAncestorCell = true; + } + + // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed. + + // It would be nice to check that |ComputedBSize != NS_UNCONSTRAINEDSIZE| + // &&ed with the percentage bsize check. However, this doesn't get + // along with table special bsize reflows, since a special bsize + // reflow (a quirk that makes such percentage height work on children + // of table cells) can cause not just a single percentage height to + // become fixed, but an entire descendant chain of percentage height + // to become fixed. + if (dependsOnCBBSize && mCBReflowInput) { + const ReflowInput* rs = this; + bool hitCBReflowInput = false; + do { + rs = rs->mParentReflowInput; + if (!rs) { + break; + } + + if (rs->mFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { + break; // no need to go further + } + rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); + + // Keep track of whether we've hit the containing block, because + // we need to go at least that far. + if (rs == mCBReflowInput) { + hitCBReflowInput = true; + } + + // XXX What about orthogonal flows? It doesn't make sense to + // keep propagating this bit across an orthogonal boundary, + // where the meaning of BSize changes. Bug 1175517. + } while (!hitCBReflowInput || + (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && + !IsQuirkContainingBlockHeight(rs, rs->mFrame->Type()))); + // Note: We actually don't need to set the + // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases + // where we hit the early break statements in + // CalcQuirkContainingBlockHeight. But it doesn't hurt + // us to set the bit in these cases. + } + if (mFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) { + // If we're reflowing everything, then we'll find out if we need + // to re-set this. + mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); + } +} + +void ReflowInput::InitDynamicReflowRoot() { + if (mFrame->CanBeDynamicReflowRoot()) { + mFrame->AddStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT); + } else { + mFrame->RemoveStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT); + } +} + +bool ReflowInput::ShouldApplyAutomaticMinimumOnBlockAxis() const { + MOZ_ASSERT(!mFrame->IsFrameOfType(nsIFrame::eReplacedSizing)); + return mFlags.mIsBSizeSetByAspectRatio && + !mStyleDisplay->IsScrollableOverflow() && + mStylePosition->MinBSize(GetWritingMode()).IsAuto(); +} + +bool ReflowInput::IsInFragmentedContext() const { + // We consider mFrame with a prev-in-flow being in a fragmented context + // because nsColumnSetFrame can reflow its last column with an unconstrained + // available block-size. + return AvailableBSize() != NS_UNCONSTRAINEDSIZE || mFrame->GetPrevInFlow(); +} + +/* static */ +LogicalMargin ReflowInput::ComputeRelativeOffsets(WritingMode aWM, + nsIFrame* aFrame, + const LogicalSize& aCBSize) { + LogicalMargin offsets(aWM); + const nsStylePosition* position = aFrame->StylePosition(); + + // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart' + // moves the boxes to the end of the line, and 'inlineEnd' moves the + // boxes to the start of the line. The computed values are always: + // inlineStart=-inlineEnd + const auto& inlineStart = position->mOffset.GetIStart(aWM); + const auto& inlineEnd = position->mOffset.GetIEnd(aWM); + bool inlineStartIsAuto = inlineStart.IsAuto(); + bool inlineEndIsAuto = inlineEnd.IsAuto(); + + // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're + // over-constrained and we ignore one of them + if (!inlineStartIsAuto && !inlineEndIsAuto) { + inlineEndIsAuto = true; + } + + if (inlineStartIsAuto) { + if (inlineEndIsAuto) { + // If both are 'auto' (their initial values), the computed values are 0 + offsets.IStart(aWM) = offsets.IEnd(aWM) = 0; + } else { + // 'inlineEnd' isn't 'auto' so compute its value + offsets.IEnd(aWM) = + nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(aWM), inlineEnd); + + // Computed value for 'inlineStart' is minus the value of 'inlineEnd' + offsets.IStart(aWM) = -offsets.IEnd(aWM); + } + + } else { + NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint"); + + // 'InlineStart' isn't 'auto' so compute its value + offsets.IStart(aWM) = + nsLayoutUtils::ComputeCBDependentValue(aCBSize.ISize(aWM), inlineStart); + + // Computed value for 'inlineEnd' is minus the value of 'inlineStart' + offsets.IEnd(aWM) = -offsets.IStart(aWM); + } + + // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart' + // and 'blockEnd' properties move relatively positioned elements in + // the block progression direction. They also must be each other's + // negative + const auto& blockStart = position->mOffset.GetBStart(aWM); + const auto& blockEnd = position->mOffset.GetBEnd(aWM); + bool blockStartIsAuto = blockStart.IsAuto(); + bool blockEndIsAuto = blockEnd.IsAuto(); + + // Check for percentage based values and a containing block block-size + // that depends on the content block-size. Treat them like 'auto' + if (NS_UNCONSTRAINEDSIZE == aCBSize.BSize(aWM)) { + if (blockStart.HasPercent()) { + blockStartIsAuto = true; + } + if (blockEnd.HasPercent()) { + blockEndIsAuto = true; + } + } + + // If neither is 'auto', 'block-end' is ignored + if (!blockStartIsAuto && !blockEndIsAuto) { + blockEndIsAuto = true; + } + + if (blockStartIsAuto) { + if (blockEndIsAuto) { + // If both are 'auto' (their initial values), the computed values are 0 + offsets.BStart(aWM) = offsets.BEnd(aWM) = 0; + } else { + // 'blockEnd' isn't 'auto' so compute its value + offsets.BEnd(aWM) = nsLayoutUtils::ComputeBSizeDependentValue( + aCBSize.BSize(aWM), blockEnd); + + // Computed value for 'blockStart' is minus the value of 'blockEnd' + offsets.BStart(aWM) = -offsets.BEnd(aWM); + } + + } else { + NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint"); + + // 'blockStart' isn't 'auto' so compute its value + offsets.BStart(aWM) = nsLayoutUtils::ComputeBSizeDependentValue( + aCBSize.BSize(aWM), blockStart); + + // Computed value for 'blockEnd' is minus the value of 'blockStart' + offsets.BEnd(aWM) = -offsets.BStart(aWM); + } + + // Convert the offsets to physical coordinates and store them on the frame + const nsMargin physicalOffsets = offsets.GetPhysicalMargin(aWM); + if (nsMargin* prop = + aFrame->GetProperty(nsIFrame::ComputedOffsetProperty())) { + *prop = physicalOffsets; + } else { + aFrame->AddProperty(nsIFrame::ComputedOffsetProperty(), + new nsMargin(physicalOffsets)); + } + + NS_ASSERTION(offsets.IStart(aWM) == -offsets.IEnd(aWM) && + offsets.BStart(aWM) == -offsets.BEnd(aWM), + "ComputeRelativeOffsets should return valid results!"); + + return offsets; +} + +/* static */ +void ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame, + const nsMargin& aComputedOffsets, + nsPoint* aPosition) { + if (!aFrame->IsRelativelyOrStickyPositioned()) { + NS_ASSERTION(!aFrame->HasProperty(nsIFrame::NormalPositionProperty()), + "We assume that changing the 'position' property causes " + "frame reconstruction. If that ever changes, this code " + "should call " + "aFrame->RemoveProperty(nsIFrame::NormalPositionProperty())"); + return; + } + + // Store the normal position + aFrame->SetProperty(nsIFrame::NormalPositionProperty(), *aPosition); + + const nsStyleDisplay* display = aFrame->StyleDisplay(); + if (StylePositionProperty::Relative == display->mPosition) { + *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); + } else if (StylePositionProperty::Sticky == display->mPosition && + !aFrame->GetNextContinuation() && !aFrame->GetPrevContinuation() && + !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) { + // Sticky positioning for elements with multiple frames needs to be + // computed all at once. We can't safely do that here because we might be + // partway through (re)positioning the frames, so leave it until the scroll + // container reflows and calls StickyScrollContainer::UpdatePositions. + // For single-frame sticky positioned elements, though, go ahead and apply + // it now to avoid unnecessary overflow updates later. + StickyScrollContainer* ssc = + StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); + if (ssc) { + *aPosition = ssc->ComputePosition(aFrame); + } + } +} + +// static +void ReflowInput::ComputeAbsPosInlineAutoMargin(nscoord aAvailMarginSpace, + WritingMode aContainingBlockWM, + bool aIsMarginIStartAuto, + bool aIsMarginIEndAuto, + LogicalMargin& aMargin, + LogicalMargin& aOffsets) { + if (aIsMarginIStartAuto) { + if (aIsMarginIEndAuto) { + if (aAvailMarginSpace < 0) { + // Note that this case is different from the neither-'auto' + // case below, where the spec says to ignore 'left'/'right'. + // Ignore the specified value for 'margin-right'. + aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace; + } else { + // Both 'margin-left' and 'margin-right' are 'auto', so they get + // equal values + aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace / 2; + aMargin.IEnd(aContainingBlockWM) = + aAvailMarginSpace - aMargin.IStart(aContainingBlockWM); + } + } else { + // Just 'margin-left' is 'auto' + aMargin.IStart(aContainingBlockWM) = aAvailMarginSpace; + } + } else { + if (aIsMarginIEndAuto) { + // Just 'margin-right' is 'auto' + aMargin.IEnd(aContainingBlockWM) = aAvailMarginSpace; + } else { + // We're over-constrained so use the direction of the containing + // block to dictate which value to ignore. (And note that the + // spec says to ignore 'left' or 'right' rather than + // 'margin-left' or 'margin-right'.) + // Note that this case is different from the both-'auto' case + // above, where the spec says to ignore + // 'margin-left'/'margin-right'. + // Ignore the specified value for 'right'. + aOffsets.IEnd(aContainingBlockWM) += aAvailMarginSpace; + } + } +} + +// static +void ReflowInput::ComputeAbsPosBlockAutoMargin(nscoord aAvailMarginSpace, + WritingMode aContainingBlockWM, + bool aIsMarginBStartAuto, + bool aIsMarginBEndAuto, + LogicalMargin& aMargin, + LogicalMargin& aOffsets) { + if (aIsMarginBStartAuto) { + if (aIsMarginBEndAuto) { + // Both 'margin-top' and 'margin-bottom' are 'auto', so they get + // equal values + aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace / 2; + aMargin.BEnd(aContainingBlockWM) = + aAvailMarginSpace - aMargin.BStart(aContainingBlockWM); + } else { + // Just margin-block-start is 'auto' + aMargin.BStart(aContainingBlockWM) = aAvailMarginSpace; + } + } else { + if (aIsMarginBEndAuto) { + // Just margin-block-end is 'auto' + aMargin.BEnd(aContainingBlockWM) = aAvailMarginSpace; + } else { + // We're over-constrained so ignore the specified value for + // block-end. (And note that the spec says to ignore 'bottom' + // rather than 'margin-bottom'.) + aOffsets.BEnd(aContainingBlockWM) += aAvailMarginSpace; + } + } +} + +void ReflowInput::ApplyRelativePositioning( + nsIFrame* aFrame, mozilla::WritingMode aWritingMode, + const mozilla::LogicalMargin& aComputedOffsets, + mozilla::LogicalPoint* aPosition, const nsSize& aContainerSize) { + // Subtract the size of the frame from the container size that we + // use for converting between the logical and physical origins of + // the frame. This accounts for the fact that logical origins in RTL + // coordinate systems are at the top right of the frame instead of + // the top left. + nsSize frameSize = aFrame->GetSize(); + nsPoint pos = + aPosition->GetPhysicalPoint(aWritingMode, aContainerSize - frameSize); + ApplyRelativePositioning( + aFrame, aComputedOffsets.GetPhysicalMargin(aWritingMode), &pos); + *aPosition = + mozilla::LogicalPoint(aWritingMode, pos, aContainerSize - frameSize); +} + +nsIFrame* ReflowInput::GetHypotheticalBoxContainer(nsIFrame* aFrame, + nscoord& aCBIStartEdge, + LogicalSize& aCBSize) const { + aFrame = aFrame->GetContainingBlock(); + NS_ASSERTION(aFrame != mFrame, "How did that happen?"); + + /* Now aFrame is the containing block we want */ + + /* Check whether the containing block is currently being reflowed. + If so, use the info from the reflow input. */ + const ReflowInput* reflowInput; + if (aFrame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) { + for (reflowInput = mParentReflowInput; + reflowInput && reflowInput->mFrame != aFrame; + reflowInput = reflowInput->mParentReflowInput) { + /* do nothing */ + } + } else { + reflowInput = nullptr; + } + + if (reflowInput) { + WritingMode wm = reflowInput->GetWritingMode(); + NS_ASSERTION(wm == aFrame->GetWritingMode(), "unexpected writing mode"); + aCBIStartEdge = reflowInput->ComputedLogicalBorderPadding(wm).IStart(wm); + aCBSize = reflowInput->ComputedSize(wm); + } else { + /* Didn't find a reflow reflowInput for aFrame. Just compute the + information we want, on the assumption that aFrame already knows its + size. This really ought to be true by now. */ + NS_ASSERTION(!aFrame->HasAnyStateBits(NS_FRAME_IN_REFLOW), + "aFrame shouldn't be in reflow; we'll lie if it is"); + WritingMode wm = aFrame->GetWritingMode(); + // Compute CB's offset & content-box size by subtracting borderpadding from + // frame size. + const auto& bp = aFrame->GetLogicalUsedBorderAndPadding(wm); + aCBIStartEdge = bp.IStart(wm); + aCBSize = aFrame->GetLogicalSize(wm) - bp.Size(wm); + } + + return aFrame; +} + +struct nsHypotheticalPosition { + // offset from inline-start edge of containing block (which is a padding edge) + nscoord mIStart; + // offset from block-start edge of containing block (which is a padding edge) + nscoord mBStart; + WritingMode mWritingMode; +}; + +/** + * aInsideBoxSizing returns the part of the padding, border, and margin + * in the aAxis dimension that goes inside the edge given by box-sizing; + * aOutsideBoxSizing returns the rest. + */ +void ReflowInput::CalculateBorderPaddingMargin( + LogicalAxis aAxis, nscoord aContainingBlockSize, nscoord* aInsideBoxSizing, + nscoord* aOutsideBoxSizing) const { + WritingMode wm = GetWritingMode(); + mozilla::Side startSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart)); + mozilla::Side endSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd)); + + nsMargin styleBorder = mStyleBorder->GetComputedBorder(); + nscoord borderStartEnd = + styleBorder.Side(startSide) + styleBorder.Side(endSide); + + nscoord paddingStartEnd, marginStartEnd; + + // See if the style system can provide us the padding directly + const auto* stylePadding = mFrame->StylePadding(); + if (nsMargin padding; stylePadding->GetPadding(padding)) { + paddingStartEnd = padding.Side(startSide) + padding.Side(endSide); + } else { + // We have to compute the start and end values + nscoord start, end; + start = nsLayoutUtils::ComputeCBDependentValue( + aContainingBlockSize, stylePadding->mPadding.Get(startSide)); + end = nsLayoutUtils::ComputeCBDependentValue( + aContainingBlockSize, stylePadding->mPadding.Get(endSide)); + paddingStartEnd = start + end; + } + + // See if the style system can provide us the margin directly + if (nsMargin margin; mStyleMargin->GetMargin(margin)) { + marginStartEnd = margin.Side(startSide) + margin.Side(endSide); + } else { + nscoord start, end; + // We have to compute the start and end values + if (mStyleMargin->mMargin.Get(startSide).IsAuto()) { + // We set this to 0 for now, and fix it up later in + // InitAbsoluteConstraints (which is caller of this function, via + // CalculateHypotheticalPosition). + start = 0; + } else { + start = nsLayoutUtils::ComputeCBDependentValue( + aContainingBlockSize, mStyleMargin->mMargin.Get(startSide)); + } + if (mStyleMargin->mMargin.Get(endSide).IsAuto()) { + // We set this to 0 for now, and fix it up later in + // InitAbsoluteConstraints (which is caller of this function, via + // CalculateHypotheticalPosition). + end = 0; + } else { + end = nsLayoutUtils::ComputeCBDependentValue( + aContainingBlockSize, mStyleMargin->mMargin.Get(endSide)); + } + marginStartEnd = start + end; + } + + nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd; + nscoord inside = 0; + if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) { + inside = borderStartEnd + paddingStartEnd; + } + outside -= inside; + *aInsideBoxSizing = inside; + *aOutsideBoxSizing = outside; +} + +/** + * Returns true iff a pre-order traversal of the normal child + * frames rooted at aFrame finds no non-empty frame before aDescendant. + */ +static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, + nsIFrame* aDescendant, + bool* aFound) { + if (aFrame == aDescendant) { + *aFound = true; + return true; + } + if (aFrame->IsPlaceholderFrame()) { + auto ph = static_cast(aFrame); + MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty()); + ph->SetLineIsEmptySoFar(true); + } else { + if (!aFrame->IsSelfEmpty()) { + *aFound = false; + return false; + } + for (nsIFrame* f : aFrame->PrincipalChildList()) { + bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); + if (*aFound || !allEmpty) { + return allEmpty; + } + } + } + *aFound = false; + return true; +} + +static bool AxisPolarityFlipped(LogicalAxis aThisAxis, WritingMode aThisWm, + WritingMode aOtherWm) { + if (MOZ_LIKELY(aThisWm == aOtherWm)) { + // Dedicated short circuit for the common case. + return false; + } + LogicalAxis otherAxis = aThisWm.IsOrthogonalTo(aOtherWm) + ? GetOrthogonalAxis(aThisAxis) + : aThisAxis; + NS_ASSERTION( + aThisWm.PhysicalAxis(aThisAxis) == aOtherWm.PhysicalAxis(otherAxis), + "Physical axes must match!"); + Side thisStartSide = + aThisWm.PhysicalSide(MakeLogicalSide(aThisAxis, eLogicalEdgeStart)); + Side otherStartSide = + aOtherWm.PhysicalSide(MakeLogicalSide(otherAxis, eLogicalEdgeStart)); + return thisStartSide != otherStartSide; +} + +static bool InlinePolarityFlipped(WritingMode aThisWm, WritingMode aOtherWm) { + return AxisPolarityFlipped(eLogicalAxisInline, aThisWm, aOtherWm); +} + +static bool BlockPolarityFlipped(WritingMode aThisWm, WritingMode aOtherWm) { + return AxisPolarityFlipped(eLogicalAxisBlock, aThisWm, aOtherWm); +} + +// Calculate the position of the hypothetical box that the element would have +// if it were in the flow. +// The values returned are relative to the padding edge of the absolute +// containing block. The writing-mode of the hypothetical box position will +// have the same block direction as the absolute containing block, but may +// differ in inline-bidi direction. +// In the code below, |aCBReflowInput->frame| is the absolute containing block, +// while |containingBlock| is the nearest block container of the placeholder +// frame, which may be different from the absolute containing block. +void ReflowInput::CalculateHypotheticalPosition( + nsPresContext* aPresContext, nsPlaceholderFrame* aPlaceholderFrame, + const ReflowInput* aCBReflowInput, nsHypotheticalPosition& aHypotheticalPos, + LayoutFrameType aFrameType) const { + NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None, + "mOriginalDisplay has not been properly initialized"); + + // Find the nearest containing block frame to the placeholder frame, + // and its inline-start edge and width. + nscoord blockIStartContentEdge; + // Dummy writing mode for blockContentSize, will be changed as needed by + // GetHypotheticalBoxContainer. + WritingMode cbwm = aCBReflowInput->GetWritingMode(); + LogicalSize blockContentSize(cbwm); + nsIFrame* containingBlock = GetHypotheticalBoxContainer( + aPlaceholderFrame, blockIStartContentEdge, blockContentSize); + // Now blockContentSize is in containingBlock's writing mode. + + // If it's a replaced element and it has a 'auto' value for + //'inline size', see if we can get the intrinsic size. This will allow + // us to exactly determine both the inline edges + WritingMode wm = containingBlock->GetWritingMode(); + + const auto& styleISize = mStylePosition->ISize(wm); + bool isAutoISize = styleISize.IsAuto(); + Maybe intrinsicSize; + if (mFlags.mIsReplaced && isAutoISize) { + // See if we can get the intrinsic size of the element + intrinsicSize = mFrame->GetIntrinsicSize().ToSize(); + } + + // See if we can calculate what the box inline size would have been if + // the element had been in the flow + Maybe boxISize; + if (mStyleDisplay->IsOriginalDisplayInlineOutside() && !mFlags.mIsReplaced) { + // For non-replaced inline-level elements the 'inline size' property + // doesn't apply, so we don't know what the inline size would have + // been without reflowing it + + } else { + // It's either a replaced inline-level element or a block-level element + + // Determine the total amount of inline direction + // border/padding/margin that the element would have had if it had + // been in the flow. Note that we ignore any 'auto' and 'inherit' + // values + nscoord insideBoxISizing, outsideBoxISizing; + CalculateBorderPaddingMargin(eLogicalAxisInline, blockContentSize.ISize(wm), + &insideBoxISizing, &outsideBoxISizing); + + if (mFlags.mIsReplaced && isAutoISize) { + // It's a replaced element with an 'auto' inline size so the box + // inline size is its intrinsic size plus any border/padding/margin + if (intrinsicSize) { + boxISize.emplace(LogicalSize(wm, *intrinsicSize).ISize(wm) + + outsideBoxISizing + insideBoxISizing); + } + + } else if (isAutoISize) { + // The box inline size is the containing block inline size + boxISize.emplace(blockContentSize.ISize(wm)); + } else { + // We need to compute it. It's important we do this, because if it's + // percentage based this computed value may be different from the computed + // value calculated using the absolute containing block width + nscoord insideBoxBSizing, dummy; + CalculateBorderPaddingMargin(eLogicalAxisBlock, + blockContentSize.ISize(wm), + &insideBoxBSizing, &dummy); + boxISize.emplace( + ComputeISizeValue(wm, blockContentSize, + LogicalSize(wm, insideBoxISizing, insideBoxBSizing), + outsideBoxISizing, styleISize) + + insideBoxISizing + outsideBoxISizing); + } + } + + // Get the placeholder x-offset and y-offset in the coordinate + // space of its containing block + // XXXbz the placeholder is not fully reflowed yet if our containing block is + // relatively positioned... + nsSize containerSize = + containingBlock->HasAnyStateBits(NS_FRAME_IN_REFLOW) + ? aCBReflowInput->ComputedSizeAsContainerIfConstrained() + : containingBlock->GetSize(); + LogicalPoint placeholderOffset( + wm, aPlaceholderFrame->GetOffsetToIgnoringScrolling(containingBlock), + containerSize); + + // First, determine the hypothetical box's mBStart. We want to check the + // content insertion frame of containingBlock for block-ness, but make + // sure to compute all coordinates in the coordinate system of + // containingBlock. + nsBlockFrame* blockFrame = + do_QueryFrame(containingBlock->GetContentInsertionFrame()); + if (blockFrame) { + // Use a null containerSize to convert a LogicalPoint functioning as a + // vector into a physical nsPoint vector. + const nsSize nullContainerSize; + LogicalPoint blockOffset( + wm, blockFrame->GetOffsetToIgnoringScrolling(containingBlock), + nullContainerSize); + bool isValid; + nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); + if (!isValid) { + // Give up. We're probably dealing with somebody using + // position:absolute inside native-anonymous content anyway. + aHypotheticalPos.mBStart = placeholderOffset.B(wm); + } else { + NS_ASSERTION(iter.GetContainer() == blockFrame, + "Found placeholder in wrong block!"); + nsBlockFrame::LineIterator lineBox = iter.GetLine(); + + // How we determine the hypothetical box depends on whether the element + // would have been inline-level or block-level + LogicalRect lineBounds = lineBox->GetBounds().ConvertTo( + wm, lineBox->mWritingMode, lineBox->mContainerSize); + if (mStyleDisplay->IsOriginalDisplayInlineOutside()) { + // Use the block-start of the inline box which the placeholder lives in + // as the hypothetical box's block-start. + aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); + } else { + // The element would have been block-level which means it would + // be below the line containing the placeholder frame, unless + // all the frames before it are empty. In that case, it would + // have been just before this line. + // XXXbz the line box is not fully reflowed yet if our + // containing block is relatively positioned... + if (lineBox != iter.End()) { + nsIFrame* firstFrame = lineBox->mFirstChild; + bool allEmpty = false; + if (firstFrame == aPlaceholderFrame) { + aPlaceholderFrame->SetLineIsEmptySoFar(true); + allEmpty = true; + } else { + auto prev = aPlaceholderFrame->GetPrevSibling(); + if (prev && prev->IsPlaceholderFrame()) { + auto ph = static_cast(prev); + if (ph->GetLineIsEmptySoFar(&allEmpty)) { + aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); + } + } + } + if (!allEmpty) { + bool found = false; + while (firstFrame) { // See bug 223064 + allEmpty = AreAllEarlierInFlowFramesEmpty( + firstFrame, aPlaceholderFrame, &found); + if (found || !allEmpty) { + break; + } + firstFrame = firstFrame->GetNextSibling(); + } + aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); + } + NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); + + if (allEmpty) { + // The top of the hypothetical box is the top of the line + // containing the placeholder, since there is nothing in the + // line before our placeholder except empty frames. + aHypotheticalPos.mBStart = + lineBounds.BStart(wm) + blockOffset.B(wm); + } else { + // The top of the hypothetical box is just below the line + // containing the placeholder. + aHypotheticalPos.mBStart = lineBounds.BEnd(wm) + blockOffset.B(wm); + } + } else { + // Just use the placeholder's block-offset wrt the containing block + aHypotheticalPos.mBStart = placeholderOffset.B(wm); + } + } + } + } else { + // The containing block is not a block, so it's probably something + // like a XUL box, etc. + // Just use the placeholder's block-offset + aHypotheticalPos.mBStart = placeholderOffset.B(wm); + } + + // Second, determine the hypothetical box's mIStart. + // How we determine the hypothetical box depends on whether the element + // would have been inline-level or block-level + if (mStyleDisplay->IsOriginalDisplayInlineOutside() || + mFlags.mIOffsetsNeedCSSAlign) { + // The placeholder represents the IStart edge of the hypothetical box. + // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart + // edge of the Alignment Container.) + aHypotheticalPos.mIStart = placeholderOffset.I(wm); + } else { + aHypotheticalPos.mIStart = blockIStartContentEdge; + } + + // The current coordinate space is that of the nearest block to the + // placeholder. Convert to the coordinate space of the absolute containing + // block. + nsPoint cbOffset = + containingBlock->GetOffsetToIgnoringScrolling(aCBReflowInput->mFrame); + + nsSize reflowSize = aCBReflowInput->ComputedSizeAsContainerIfConstrained(); + LogicalPoint logCBOffs(wm, cbOffset, reflowSize - containerSize); + aHypotheticalPos.mIStart += logCBOffs.I(wm); + aHypotheticalPos.mBStart += logCBOffs.B(wm); + + // If block direction doesn't match (whether orthogonal or antiparallel), + // we'll have to convert aHypotheticalPos to be in terms of cbwm. + // This upcoming conversion must be taken into account for border offsets. + const bool hypotheticalPosWillUseCbwm = + cbwm.GetBlockDir() != wm.GetBlockDir(); + // The specified offsets are relative to the absolute containing block's + // padding edge and our current values are relative to the border edge, so + // translate. + const LogicalMargin border = aCBReflowInput->ComputedLogicalBorder(wm); + if (hypotheticalPosWillUseCbwm && InlinePolarityFlipped(wm, cbwm)) { + aHypotheticalPos.mIStart += border.IEnd(wm); + } else { + aHypotheticalPos.mIStart -= border.IStart(wm); + } + + if (hypotheticalPosWillUseCbwm && BlockPolarityFlipped(wm, cbwm)) { + aHypotheticalPos.mBStart += border.BEnd(wm); + } else { + aHypotheticalPos.mBStart -= border.BStart(wm); + } + // At this point, we have computed aHypotheticalPos using the writing mode + // of the placeholder's containing block. + + if (hypotheticalPosWillUseCbwm) { + // If the block direction we used in calculating aHypotheticalPos does not + // match the absolute containing block's, we need to convert here so that + // aHypotheticalPos is usable in relation to the absolute containing block. + // This requires computing or measuring the abspos frame's block-size, + // which is not otherwise required/used here (as aHypotheticalPos + // records only the block-start coordinate). + + // This is similar to the inline-size calculation for a replaced + // inline-level element or a block-level element (above), except that + // 'auto' sizing is handled differently in the block direction for non- + // replaced elements and replaced elements lacking an intrinsic size. + + // Determine the total amount of block direction + // border/padding/margin that the element would have had if it had + // been in the flow. Note that we ignore any 'auto' and 'inherit' + // values. + nscoord insideBoxSizing, outsideBoxSizing; + CalculateBorderPaddingMargin(eLogicalAxisBlock, blockContentSize.BSize(wm), + &insideBoxSizing, &outsideBoxSizing); + + nscoord boxBSize; + const auto& styleBSize = mStylePosition->BSize(wm); + if (styleBSize.BehavesLikeInitialValueOnBlockAxis()) { + if (mFlags.mIsReplaced && intrinsicSize) { + // It's a replaced element with an 'auto' block size so the box + // block size is its intrinsic size plus any border/padding/margin + boxBSize = LogicalSize(wm, *intrinsicSize).BSize(wm) + + outsideBoxSizing + insideBoxSizing; + } else { + // XXX Bug 1191801 + // Figure out how to get the correct boxBSize here (need to reflow the + // positioned frame?) + boxBSize = 0; + } + } else { + // We need to compute it. It's important we do this, because if it's + // percentage-based this computed value may be different from the + // computed value calculated using the absolute containing block height. + boxBSize = nsLayoutUtils::ComputeBSizeValue( + blockContentSize.BSize(wm), insideBoxSizing, + styleBSize.AsLengthPercentage()) + + insideBoxSizing + outsideBoxSizing; + } + + LogicalSize boxSize(wm, boxISize.valueOr(0), boxBSize); + + LogicalPoint origin(wm, aHypotheticalPos.mIStart, aHypotheticalPos.mBStart); + origin = + origin.ConvertTo(cbwm, wm, reflowSize - boxSize.GetPhysicalSize(wm)); + + aHypotheticalPos.mIStart = origin.I(cbwm); + aHypotheticalPos.mBStart = origin.B(cbwm); + aHypotheticalPos.mWritingMode = cbwm; + } else { + aHypotheticalPos.mWritingMode = wm; + } +} + +bool ReflowInput::IsInlineSizeComputableByBlockSizeAndAspectRatio( + nscoord aBlockSize) const { + WritingMode wm = GetWritingMode(); + MOZ_ASSERT(!mStylePosition->mOffset.GetBStart(wm).IsAuto() && + !mStylePosition->mOffset.GetBEnd(wm).IsAuto(), + "If any of the block-start and block-end are auto, aBlockSize " + "doesn't make sense"); + NS_WARNING_ASSERTION( + aBlockSize >= 0 && aBlockSize != NS_UNCONSTRAINEDSIZE, + "The caller shouldn't give us an unresolved or invalid block size"); + + if (!mStylePosition->mAspectRatio.HasFiniteRatio()) { + return false; + } + + // We don't have to compute the inline size by aspect-ratio and the resolved + // block size (from insets) for replaced elements. + if (mFrame->IsFrameOfType(nsIFrame::eReplaced)) { + return false; + } + + // If inline size is specified, we should have it by mFrame->ComputeSize() + // already. + if (mStylePosition->ISize(wm).IsLengthPercentage()) { + return false; + } + + // If both inline insets are non-auto, mFrame->ComputeSize() should get a + // possible inline size by those insets, so we don't rely on aspect-ratio. + if (!mStylePosition->mOffset.GetIStart(wm).IsAuto() && + !mStylePosition->mOffset.GetIEnd(wm).IsAuto()) { + return false; + } + + // Just an error handling. If |aBlockSize| is NS_UNCONSTRAINEDSIZE, there must + // be something wrong, and we don't want to continue the calculation for + // aspect-ratio. So we return false if this happens. + return aBlockSize != NS_UNCONSTRAINEDSIZE; +} + +// FIXME: Move this into nsIFrame::ComputeSize() if possible, so most of the +// if-checks can be simplier. +LogicalSize ReflowInput::CalculateAbsoluteSizeWithResolvedAutoBlockSize( + nscoord aAutoBSize, const LogicalSize& aTentativeComputedSize) { + LogicalSize resultSize = aTentativeComputedSize; + WritingMode wm = GetWritingMode(); + + // Two cases we don't want to early return: + // 1. If the block size behaves as initial value and we haven't resolved it in + // ComputeSize() yet, we need to apply |aAutoBSize|. + // Also, we check both computed style and |resultSize.BSize(wm)| to avoid + // applying |aAutoBSize| when the resolved block size is saturated at + // nscoord_MAX, and wrongly treated as NS_UNCONSTRAINEDSIZE because of a + // giant specified block-size. + // 2. If the block size needs to be computed via aspect-ratio and + // |aAutoBSize|, we need to apply |aAutoBSize|. In this case, + // |resultSize.BSize(wm)| may not be NS_UNCONSTRAINEDSIZE because we apply + // aspect-ratio in ComputeSize() for block axis by default, so we have to + // check its computed style. + const bool bSizeBehavesAsInitial = + mStylePosition->BSize(wm).BehavesLikeInitialValueOnBlockAxis(); + const bool bSizeIsStillUnconstrained = + bSizeBehavesAsInitial && resultSize.BSize(wm) == NS_UNCONSTRAINEDSIZE; + const bool needsComputeInlineSizeByAspectRatio = + bSizeBehavesAsInitial && + IsInlineSizeComputableByBlockSizeAndAspectRatio(aAutoBSize); + if (!bSizeIsStillUnconstrained && !needsComputeInlineSizeByAspectRatio) { + return resultSize; + } + + // For non-replaced elements with block-size auto, the block-size + // fills the remaining space, and we clamp it by min/max size constraints. + resultSize.BSize(wm) = ApplyMinMaxBSize(aAutoBSize); + + if (!needsComputeInlineSizeByAspectRatio) { + return resultSize; + } + + // Calculate transferred inline size through aspect-ratio. + // For non-replaced elements, we always take box-sizing into account. + const auto boxSizingAdjust = + mStylePosition->mBoxSizing == StyleBoxSizing::Border + ? ComputedLogicalBorderPadding(wm).Size(wm) + : LogicalSize(wm); + auto transferredISize = + mStylePosition->mAspectRatio.ToLayoutRatio().ComputeRatioDependentSize( + LogicalAxis::eLogicalAxisInline, wm, aAutoBSize, boxSizingAdjust); + resultSize.ISize(wm) = ApplyMinMaxISize(transferredISize); + + MOZ_ASSERT(mFlags.mIsBSizeSetByAspectRatio, + "This flag should have been set because nsIFrame::ComputeSize() " + "returns AspectRatioUsage::ToComputeBSize unconditionally for " + "auto block-size"); + mFlags.mIsBSizeSetByAspectRatio = false; + + return resultSize; +} + +void ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext, + const ReflowInput* aCBReflowInput, + const LogicalSize& aCBSize, + LayoutFrameType aFrameType) { + WritingMode wm = GetWritingMode(); + WritingMode cbwm = aCBReflowInput->GetWritingMode(); + NS_WARNING_ASSERTION(aCBSize.BSize(cbwm) != NS_UNCONSTRAINEDSIZE, + "containing block bsize must be constrained"); + + NS_ASSERTION(aFrameType != LayoutFrameType::Table, + "InitAbsoluteConstraints should not be called on table frames"); + NS_ASSERTION(mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW), + "Why are we here?"); + + const auto& styleOffset = mStylePosition->mOffset; + bool iStartIsAuto = styleOffset.GetIStart(cbwm).IsAuto(); + bool iEndIsAuto = styleOffset.GetIEnd(cbwm).IsAuto(); + bool bStartIsAuto = styleOffset.GetBStart(cbwm).IsAuto(); + bool bEndIsAuto = styleOffset.GetBEnd(cbwm).IsAuto(); + + // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are + // 'auto', then compute the hypothetical box position where the element would + // have been if it had been in the flow + nsHypotheticalPosition hypotheticalPos; + if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) { + nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame(); + MOZ_ASSERT(placeholderFrame, "no placeholder frame"); + nsIFrame* placeholderParent = placeholderFrame->GetParent(); + MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders"); + + if (placeholderFrame->HasAnyStateBits( + PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) { + MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(), + "This flag should only be set on grid/flex children"); + // If the (as-yet unknown) static position will determine the inline + // and/or block offsets, set flags to note those offsets aren't valid + // until we can do CSS Box Alignment on the OOF frame. + mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto); + mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto); + } + + if (mFlags.mStaticPosIsCBOrigin) { + hypotheticalPos.mWritingMode = cbwm; + hypotheticalPos.mIStart = nscoord(0); + hypotheticalPos.mBStart = nscoord(0); + if (placeholderParent->IsGridContainerFrame() && + placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY | + NS_STATE_GRID_IS_ROW_MASONRY)) { + // Disable CSS alignment in Masonry layout since we don't have real grid + // areas in that axis. We'll use the placeholder position instead as it + // was calculated by nsGridContainerFrame::MasonryLayout. + auto cbsz = aCBSize.GetPhysicalSize(cbwm); + LogicalPoint pos = placeholderFrame->GetLogicalPosition(cbwm, cbsz); + if (placeholderParent->HasAnyStateBits(NS_STATE_GRID_IS_COL_MASONRY)) { + mFlags.mIOffsetsNeedCSSAlign = false; + hypotheticalPos.mIStart = pos.I(cbwm); + } else { + mFlags.mBOffsetsNeedCSSAlign = false; + hypotheticalPos.mBStart = pos.B(cbwm); + } + } + } else { + // XXXmats all this is broken for orthogonal writing-modes: bug 1521988. + CalculateHypotheticalPosition(aPresContext, placeholderFrame, + aCBReflowInput, hypotheticalPos, + aFrameType); + if (aCBReflowInput->mFrame->IsGridContainerFrame()) { + // 'hypotheticalPos' is relative to the padding rect of the CB *frame*. + // In grid layout the CB is the grid area rectangle, so we translate + // 'hypotheticalPos' to be relative that rectangle here. + nsRect cb = nsGridContainerFrame::GridItemCB(mFrame); + nscoord left(0); + nscoord right(0); + if (cbwm.IsBidiLTR()) { + left = cb.X(); + } else { + right = aCBReflowInput->ComputedWidth() + + aCBReflowInput->ComputedPhysicalPadding().LeftRight() - + cb.XMost(); + } + LogicalMargin offsets(cbwm, nsMargin(cb.Y(), right, nscoord(0), left)); + hypotheticalPos.mIStart -= offsets.IStart(cbwm); + hypotheticalPos.mBStart -= offsets.BStart(cbwm); + } + } + } + + // Initialize the 'left' and 'right' computed offsets + // XXX Handle new 'static-position' value... + + // Size of the containing block in its writing mode + LogicalSize cbSize = aCBSize; + LogicalMargin offsets = ComputedLogicalOffsets(cbwm); + + if (iStartIsAuto) { + offsets.IStart(cbwm) = 0; + } else { + offsets.IStart(cbwm) = nsLayoutUtils::ComputeCBDependentValue( + cbSize.ISize(cbwm), styleOffset.GetIStart(cbwm)); + } + if (iEndIsAuto) { + offsets.IEnd(cbwm) = 0; + } else { + offsets.IEnd(cbwm) = nsLayoutUtils::ComputeCBDependentValue( + cbSize.ISize(cbwm), styleOffset.GetIEnd(cbwm)); + } + + if (iStartIsAuto && iEndIsAuto) { + if (cbwm.IsBidiLTR() != hypotheticalPos.mWritingMode.IsBidiLTR()) { + offsets.IEnd(cbwm) = hypotheticalPos.mIStart; + iEndIsAuto = false; + } else { + offsets.IStart(cbwm) = hypotheticalPos.mIStart; + iStartIsAuto = false; + } + } + + if (bStartIsAuto) { + offsets.BStart(cbwm) = 0; + } else { + offsets.BStart(cbwm) = nsLayoutUtils::ComputeBSizeDependentValue( + cbSize.BSize(cbwm), styleOffset.GetBStart(cbwm)); + } + if (bEndIsAuto) { + offsets.BEnd(cbwm) = 0; + } else { + offsets.BEnd(cbwm) = nsLayoutUtils::ComputeBSizeDependentValue( + cbSize.BSize(cbwm), styleOffset.GetBEnd(cbwm)); + } + + if (bStartIsAuto && bEndIsAuto) { + // Treat 'top' like 'static-position' + offsets.BStart(cbwm) = hypotheticalPos.mBStart; + bStartIsAuto = false; + } + + SetComputedLogicalOffsets(cbwm, offsets); + + if (wm.IsOrthogonalTo(cbwm)) { + if (bStartIsAuto || bEndIsAuto) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + } else { + if (iStartIsAuto || iEndIsAuto) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + } + + nsIFrame::SizeComputationResult sizeResult = { + LogicalSize(wm), nsIFrame::AspectRatioUsage::None}; + { + AutoMaybeDisableFontInflation an(mFrame); + + sizeResult = mFrame->ComputeSize( + mRenderingContext, wm, cbSize.ConvertTo(wm, cbwm), + cbSize.ConvertTo(wm, cbwm).ISize(wm), // XXX or AvailableISize()? + ComputedLogicalMargin(wm).Size(wm) + + ComputedLogicalOffsets(wm).Size(wm), + ComputedLogicalBorderPadding(wm).Size(wm), {}, mComputeSizeFlags); + mComputedSize = sizeResult.mLogicalSize; + NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); + NS_ASSERTION( + ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0, + "Bogus block-size"); + } + + LogicalSize& computedSize = sizeResult.mLogicalSize; + computedSize = computedSize.ConvertTo(cbwm, wm); + + mFlags.mIsBSizeSetByAspectRatio = sizeResult.mAspectRatioUsage == + nsIFrame::AspectRatioUsage::ToComputeBSize; + + // XXX Now that we have ComputeSize, can we condense many of the + // branches off of widthIsAuto? + + LogicalMargin margin = ComputedLogicalMargin(cbwm); + const LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbwm); + + bool iSizeIsAuto = mStylePosition->ISize(cbwm).IsAuto(); + bool marginIStartIsAuto = false; + bool marginIEndIsAuto = false; + bool marginBStartIsAuto = false; + bool marginBEndIsAuto = false; + if (iStartIsAuto) { + // We know 'right' is not 'auto' anymore thanks to the hypothetical + // box code above. + // Solve for 'left'. + if (iSizeIsAuto) { + // XXXldb This, and the corresponding code in + // nsAbsoluteContainingBlock.cpp, could probably go away now that + // we always compute widths. + offsets.IStart(cbwm) = NS_AUTOOFFSET; + } else { + offsets.IStart(cbwm) = cbSize.ISize(cbwm) - offsets.IEnd(cbwm) - + computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - + borderPadding.IStartEnd(cbwm); + } + } else if (iEndIsAuto) { + // We know 'left' is not 'auto' anymore thanks to the hypothetical + // box code above. + // Solve for 'right'. + if (iSizeIsAuto) { + // XXXldb This, and the corresponding code in + // nsAbsoluteContainingBlock.cpp, could probably go away now that + // we always compute widths. + offsets.IEnd(cbwm) = NS_AUTOOFFSET; + } else { + offsets.IEnd(cbwm) = cbSize.ISize(cbwm) - offsets.IStart(cbwm) - + computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - + borderPadding.IStartEnd(cbwm); + } + } else if (!mFrame->HasIntrinsicKeywordForBSize() || + !wm.IsOrthogonalTo(cbwm)) { + // Neither 'inline-start' nor 'inline-end' is 'auto'. + if (wm.IsOrthogonalTo(cbwm)) { + // For orthogonal blocks, we need to handle the case where the block had + // unconstrained block-size, which mapped to unconstrained inline-size + // in the containing block's writing mode. + nscoord autoISize = cbSize.ISize(cbwm) - margin.IStartEnd(cbwm) - + borderPadding.IStartEnd(cbwm) - + offsets.IStartEnd(cbwm); + autoISize = std::max(autoISize, 0); + // FIXME: Bug 1602669: if |autoISize| happens to be numerically equal to + // NS_UNCONSTRAINEDSIZE, we may get some unexpected behavior. We need a + // better way to distinguish between unconstrained size and resolved + // size. + NS_WARNING_ASSERTION(autoISize != NS_UNCONSTRAINEDSIZE, + "Unexpected size from inline-start and inline-end"); + + nscoord autoBSizeInWM = autoISize; + LogicalSize computedSizeInWM = + CalculateAbsoluteSizeWithResolvedAutoBlockSize( + autoBSizeInWM, computedSize.ConvertTo(wm, cbwm)); + computedSize = computedSizeInWM.ConvertTo(cbwm, wm); + } + + // However, the inline-size might + // still not fill all the available space (even though we didn't + // shrink-wrap) in case: + // * inline-size was specified + // * we're dealing with a replaced element + // * width was constrained by min- or max-inline-size. + + nscoord availMarginSpace = + aCBSize.ISize(cbwm) - offsets.IStartEnd(cbwm) - margin.IStartEnd(cbwm) - + borderPadding.IStartEnd(cbwm) - computedSize.ISize(cbwm); + marginIStartIsAuto = mStyleMargin->mMargin.GetIStart(cbwm).IsAuto(); + marginIEndIsAuto = mStyleMargin->mMargin.GetIEnd(cbwm).IsAuto(); + ComputeAbsPosInlineAutoMargin(availMarginSpace, cbwm, marginIStartIsAuto, + marginIEndIsAuto, margin, offsets); + } + + bool bSizeIsAuto = + mStylePosition->BSize(cbwm).BehavesLikeInitialValueOnBlockAxis(); + if (bStartIsAuto) { + // solve for block-start + if (bSizeIsAuto) { + offsets.BStart(cbwm) = NS_AUTOOFFSET; + } else { + offsets.BStart(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - + borderPadding.BStartEnd(cbwm) - + computedSize.BSize(cbwm) - offsets.BEnd(cbwm); + } + } else if (bEndIsAuto) { + // solve for block-end + if (bSizeIsAuto) { + offsets.BEnd(cbwm) = NS_AUTOOFFSET; + } else { + offsets.BEnd(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - + borderPadding.BStartEnd(cbwm) - + computedSize.BSize(cbwm) - offsets.BStart(cbwm); + } + } else if (!mFrame->HasIntrinsicKeywordForBSize() || + wm.IsOrthogonalTo(cbwm)) { + // Neither block-start nor -end is 'auto'. + nscoord autoBSize = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - + borderPadding.BStartEnd(cbwm) - offsets.BStartEnd(cbwm); + autoBSize = std::max(autoBSize, 0); + // FIXME: Bug 1602669: if |autoBSize| happens to be numerically equal to + // NS_UNCONSTRAINEDSIZE, we may get some unexpected behavior. We need a + // better way to distinguish between unconstrained size and resolved size. + NS_WARNING_ASSERTION(autoBSize != NS_UNCONSTRAINEDSIZE, + "Unexpected size from block-start and block-end"); + + // For orthogonal case, the inline size in |wm| should have been handled by + // ComputeSize(). In other words, we only have to apply |autoBSize| to + // the computed size if this value can represent the block size in |wm|. + if (!wm.IsOrthogonalTo(cbwm)) { + // We handle the unconstrained block-size in current block's writing + // mode 'wm'. + LogicalSize computedSizeInWM = + CalculateAbsoluteSizeWithResolvedAutoBlockSize( + autoBSize, computedSize.ConvertTo(wm, cbwm)); + computedSize = computedSizeInWM.ConvertTo(cbwm, wm); + } + + // The block-size might still not fill all the available space in case: + // * bsize was specified + // * we're dealing with a replaced element + // * bsize was constrained by min- or max-bsize. + nscoord availMarginSpace = autoBSize - computedSize.BSize(cbwm); + marginBStartIsAuto = mStyleMargin->mMargin.GetBStart(cbwm).IsAuto(); + marginBEndIsAuto = mStyleMargin->mMargin.GetBEnd(cbwm).IsAuto(); + + ComputeAbsPosBlockAutoMargin(availMarginSpace, cbwm, marginBStartIsAuto, + marginBEndIsAuto, margin, offsets); + } + mComputedSize = computedSize.ConvertTo(wm, cbwm); + + SetComputedLogicalOffsets(cbwm, offsets); + SetComputedLogicalMargin(cbwm, margin); + + // If we have auto margins, update our UsedMarginProperty. The property + // will have already been created by InitOffsets if it is needed. + if (marginIStartIsAuto || marginIEndIsAuto || marginBStartIsAuto || + marginBEndIsAuto) { + nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty()); + MOZ_ASSERT(propValue, + "UsedMarginProperty should have been created " + "by InitOffsets."); + *propValue = margin.GetPhysicalMargin(cbwm); + } +} + +// This will not be converted to abstract coordinates because it's only +// used in CalcQuirkContainingBlockHeight +static nscoord GetBlockMarginBorderPadding(const ReflowInput* aReflowInput) { + nscoord result = 0; + if (!aReflowInput) return result; + + // zero auto margins + nsMargin margin = aReflowInput->ComputedPhysicalMargin(); + if (NS_AUTOMARGIN == margin.top) margin.top = 0; + if (NS_AUTOMARGIN == margin.bottom) margin.bottom = 0; + + result += margin.top + margin.bottom; + result += aReflowInput->ComputedPhysicalBorderPadding().top + + aReflowInput->ComputedPhysicalBorderPadding().bottom; + + return result; +} + +/* Get the height based on the viewport of the containing block specified + * in aReflowInput when the containing block has mComputedHeight == + * NS_UNCONSTRAINEDSIZE This will walk up the chain of containing blocks looking + * for a computed height until it finds the canvas frame, or it encounters a + * frame that is not a block, area, or scroll frame. This handles compatibility + * with IE (see bug 85016 and bug 219693) + * + * When we encounter scrolledContent block frames, we skip over them, + * since they are guaranteed to not be useful for computing the containing + * block. + * + * See also IsQuirkContainingBlockHeight. + */ +static nscoord CalcQuirkContainingBlockHeight( + const ReflowInput* aCBReflowInput) { + const ReflowInput* firstAncestorRI = nullptr; // a candidate for html frame + const ReflowInput* secondAncestorRI = nullptr; // a candidate for body frame + + // initialize the default to NS_UNCONSTRAINEDSIZE as this is the containings + // block computed height when this function is called. It is possible that we + // don't alter this height especially if we are restricted to one level + nscoord result = NS_UNCONSTRAINEDSIZE; + + const ReflowInput* ri = aCBReflowInput; + for (; ri; ri = ri->mParentReflowInput) { + LayoutFrameType frameType = ri->mFrame->Type(); + // if the ancestor is auto height then skip it and continue up if it + // is the first block frame and possibly the body/html + if (LayoutFrameType::Block == frameType || + LayoutFrameType::Scroll == frameType) { + secondAncestorRI = firstAncestorRI; + firstAncestorRI = ri; + + // If the current frame we're looking at is positioned, we don't want to + // go any further (see bug 221784). The behavior we want here is: 1) If + // not auto-height, use this as the percentage base. 2) If auto-height, + // keep looking, unless the frame is positioned. + if (NS_UNCONSTRAINEDSIZE == ri->ComputedHeight()) { + if (ri->mFrame->IsAbsolutelyPositioned(ri->mStyleDisplay)) { + break; + } else { + continue; + } + } + } else if (LayoutFrameType::Canvas == frameType) { + // Always continue on to the height calculation + } else if (LayoutFrameType::PageContent == frameType) { + nsIFrame* prevInFlow = ri->mFrame->GetPrevInFlow(); + // only use the page content frame for a height basis if it is the first + // in flow + if (prevInFlow) break; + } else { + break; + } + + // if the ancestor is the page content frame then the percent base is + // the avail height, otherwise it is the computed height + result = (LayoutFrameType::PageContent == frameType) ? ri->AvailableHeight() + : ri->ComputedHeight(); + // if unconstrained - don't sutract borders - would result in huge height + if (NS_UNCONSTRAINEDSIZE == result) return result; + + // if we got to the canvas or page content frame, then subtract out + // margin/border/padding for the BODY and HTML elements + if ((LayoutFrameType::Canvas == frameType) || + (LayoutFrameType::PageContent == frameType)) { + result -= GetBlockMarginBorderPadding(firstAncestorRI); + result -= GetBlockMarginBorderPadding(secondAncestorRI); + +#ifdef DEBUG + // make sure the first ancestor is the HTML and the second is the BODY + if (firstAncestorRI) { + nsIContent* frameContent = firstAncestorRI->mFrame->GetContent(); + if (frameContent) { + NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html), + "First ancestor is not HTML"); + } + } + if (secondAncestorRI) { + nsIContent* frameContent = secondAncestorRI->mFrame->GetContent(); + if (frameContent) { + NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body), + "Second ancestor is not BODY"); + } + } +#endif + + } + // if we got to the html frame (a block child of the canvas) ... + else if (LayoutFrameType::Block == frameType && ri->mParentReflowInput && + ri->mParentReflowInput->mFrame->IsCanvasFrame()) { + // ... then subtract out margin/border/padding for the BODY element + result -= GetBlockMarginBorderPadding(secondAncestorRI); + } + break; + } + + // Make sure not to return a negative height here! + return std::max(result, 0); +} + +// Called by InitConstraints() to compute the containing block rectangle for +// the element. Handles the special logic for absolutely positioned elements +LogicalSize ReflowInput::ComputeContainingBlockRectangle( + nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const { + // Unless the element is absolutely positioned, the containing block is + // formed by the content edge of the nearest block-level ancestor + LogicalSize cbSize = aContainingBlockRI->ComputedSize(); + + WritingMode wm = aContainingBlockRI->GetWritingMode(); + + if (aContainingBlockRI->mFlags.mTreatBSizeAsIndefinite) { + cbSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; + } + + if (((mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && + // XXXfr hack for making frames behave properly when in overflow + // container lists, see bug 154892; need to revisit later + !mFrame->GetPrevInFlow()) || + (mFrame->IsTableFrame() && + mFrame->GetParent()->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) && + mStyleDisplay->IsAbsolutelyPositioned(mFrame)) { + // See if the ancestor is block-level or inline-level + const auto computedPadding = aContainingBlockRI->ComputedLogicalPadding(wm); + if (aContainingBlockRI->mStyleDisplay->IsInlineOutsideStyle()) { + // Base our size on the actual size of the frame. In cases when this is + // completely bogus (eg initial reflow), this code shouldn't even be + // called, since the code in nsInlineFrame::Reflow will pass in + // the containing block dimensions to our constructor. + // XXXbz we should be taking the in-flows into account too, but + // that's very hard. + + LogicalMargin computedBorder = + aContainingBlockRI->ComputedLogicalBorderPadding(wm) - + computedPadding; + cbSize.ISize(wm) = + aContainingBlockRI->mFrame->ISize(wm) - computedBorder.IStartEnd(wm); + NS_ASSERTION(cbSize.ISize(wm) >= 0, "Negative containing block isize!"); + cbSize.BSize(wm) = + aContainingBlockRI->mFrame->BSize(wm) - computedBorder.BStartEnd(wm); + NS_ASSERTION(cbSize.BSize(wm) >= 0, "Negative containing block bsize!"); + } else { + // If the ancestor is block-level, the containing block is formed by the + // padding edge of the ancestor + cbSize += computedPadding.Size(wm); + } + } else { + auto IsQuirky = [](const StyleSize& aSize) -> bool { + return aSize.ConvertsToPercentage(); + }; + // an element in quirks mode gets a containing block based on looking for a + // parent with a non-auto height if the element has a percent height. + // Note: We don't emulate this quirk for percents in calc(), or in vertical + // writing modes, or if the containing block is a flex or grid item. + if (!wm.IsVertical() && NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { + if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && + !aContainingBlockRI->mFrame->IsFlexOrGridItem() && + (IsQuirky(mStylePosition->mHeight) || + (mFrame->IsTableWrapperFrame() && + IsQuirky(mFrame->PrincipalChildList() + .FirstChild() + ->StylePosition() + ->mHeight)))) { + cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI); + } + } + } + + return cbSize.ConvertTo(GetWritingMode(), wm); +} + +// XXX refactor this code to have methods for each set of properties +// we are computing: width,height,line-height; margin; offsets + +void ReflowInput::InitConstraints( + nsPresContext* aPresContext, const Maybe& aContainingBlockSize, + const Maybe& aBorder, const Maybe& aPadding, + LayoutFrameType aFrameType) { + WritingMode wm = GetWritingMode(); + LogicalSize cbSize = aContainingBlockSize.valueOr( + LogicalSize(mWritingMode, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)); + DISPLAY_INIT_CONSTRAINTS(mFrame, this, cbSize.ISize(wm), cbSize.BSize(wm), + aBorder, aPadding); + + // If this is a reflow root, then set the computed width and + // height equal to the available space + if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) { + // XXXldb This doesn't mean what it used to! + InitOffsets(wm, cbSize.ISize(wm), aFrameType, mComputeSizeFlags, aBorder, + aPadding, mStyleDisplay); + // Override mComputedMargin since reflow roots start from the + // frame's boundary, which is inside the margin. + SetComputedLogicalMargin(wm, LogicalMargin(wm)); + SetComputedLogicalOffsets(wm, LogicalMargin(wm)); + + const auto borderPadding = ComputedLogicalBorderPadding(wm); + SetComputedISize( + std::max(0, AvailableISize() - borderPadding.IStartEnd(wm)), + ResetResizeFlags::No); + SetComputedBSize( + AvailableBSize() != NS_UNCONSTRAINEDSIZE + ? std::max(0, AvailableBSize() - borderPadding.BStartEnd(wm)) + : NS_UNCONSTRAINEDSIZE, + ResetResizeFlags::No); + + mComputedMinSize.SizeTo(mWritingMode, 0, 0); + mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE, + NS_UNCONSTRAINEDSIZE); + } else { + // Get the containing block's reflow input + const ReflowInput* cbri = mCBReflowInput; + MOZ_ASSERT(cbri, "no containing block"); + MOZ_ASSERT(mFrame->GetParent()); + + // If we weren't given a containing block size, then compute one. + if (aContainingBlockSize.isNothing()) { + cbSize = ComputeContainingBlockRectangle(aPresContext, cbri); + } + + // See if the containing block height is based on the size of its + // content + if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { + // See if the containing block is a cell frame which needs + // to use the mComputedHeight of the cell instead of what the cell block + // passed in. + // XXX It seems like this could lead to bugs with min-height and friends + if (cbri->mParentReflowInput && cbri->mFrame->IsTableCellFrame()) { + cbSize.BSize(wm) = cbri->ComputedSize(wm).BSize(wm); + } + } + + // XXX Might need to also pass the CB height (not width) for page boxes, + // too, if we implement them. + + // For calculating positioning offsets, margins, borders and + // padding, we use the writing mode of the containing block + WritingMode cbwm = cbri->GetWritingMode(); + InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm), aFrameType, + mComputeSizeFlags, aBorder, aPadding, mStyleDisplay); + + // For calculating the size of this box, we use its own writing mode + const auto& blockSize = mStylePosition->BSize(wm); + bool isAutoBSize = blockSize.BehavesLikeInitialValueOnBlockAxis(); + + // Check for a percentage based block size and a containing block + // block size that depends on the content block size + if (blockSize.HasPercent()) { + if (NS_UNCONSTRAINEDSIZE == cbSize.BSize(wm)) { + // this if clause enables %-blockSize on replaced inline frames, + // such as images. See bug 54119. The else clause "blockSizeUnit = + // eStyleUnit_Auto;" used to be called exclusively. + if (mFlags.mIsReplaced && mStyleDisplay->IsInlineOutsideStyle()) { + // Get the containing block's reflow input + NS_ASSERTION(nullptr != cbri, "no containing block"); + // in quirks mode, get the cb height using the special quirk method + if (!wm.IsVertical() && + eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { + if (!cbri->mFrame->IsTableCellFrame() && + !cbri->mFrame->IsFlexOrGridItem()) { + cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri); + if (cbSize.BSize(wm) == NS_UNCONSTRAINEDSIZE) { + isAutoBSize = true; + } + } else { + isAutoBSize = true; + } + } + // in standard mode, use the cb block size. if it's "auto", + // as will be the case by default in BODY, use auto block size + // as per CSS2 spec. + else { + nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm); + if (NS_UNCONSTRAINEDSIZE != computedBSize) { + cbSize.BSize(wm) = computedBSize; + } else { + isAutoBSize = true; + } + } + } else { + // default to interpreting the blockSize like 'auto' + isAutoBSize = true; + } + } + } + + // Compute our offsets if the element is relatively positioned. We + // need the correct containing block inline-size and block-size + // here, which is why we need to do it after all the quirks-n-such + // above. (If the element is sticky positioned, we need to wait + // until the scroll container knows its size, so we compute offsets + // from StickyScrollContainer::UpdatePositions.) + if (mStyleDisplay->IsRelativelyPositioned(mFrame)) { + const LogicalMargin offsets = + ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm)); + SetComputedLogicalOffsets(cbwm, offsets); + } else { + // Initialize offsets to 0 + SetComputedLogicalOffsets(wm, LogicalMargin(wm)); + } + + // Calculate the computed values for min and max properties. Note that + // this MUST come after we've computed our border and padding. + ComputeMinMaxValues(cbSize); + + // Calculate the computed inlineSize and blockSize. + // This varies by frame type. + + if (IsInternalTableFrame()) { + // Internal table elements. The rules vary depending on the type. + // Calculate the computed isize + bool rowOrRowGroup = false; + const auto& inlineSize = mStylePosition->ISize(wm); + bool isAutoISize = inlineSize.IsAuto(); + if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) || + (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) { + // 'inlineSize' property doesn't apply to table rows and row groups + isAutoISize = true; + rowOrRowGroup = true; + } + + // calc() with both percentages and lengths act like auto on internal + // table elements + if (isAutoISize || inlineSize.HasLengthAndPercentage()) { + if (AvailableISize() != NS_UNCONSTRAINEDSIZE && !rowOrRowGroup) { + // Internal table elements don't have margins. Only tables and + // cells have border and padding + SetComputedISize( + std::max(0, AvailableISize() - + ComputedLogicalBorderPadding(wm).IStartEnd(wm)), + ResetResizeFlags::No); + } else { + SetComputedISize(AvailableISize(), ResetResizeFlags::No); + } + NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize"); + + } else { + SetComputedISize( + ComputeISizeValue(cbSize, mStylePosition->mBoxSizing, inlineSize), + ResetResizeFlags::No); + } + + // Calculate the computed block size + if (StyleDisplay::TableColumn == mStyleDisplay->mDisplay || + StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay) { + // 'blockSize' property doesn't apply to table columns and column groups + isAutoBSize = true; + } + // calc() with both percentages and lengths acts like 'auto' on internal + // table elements + if (isAutoBSize || blockSize.HasLengthAndPercentage()) { + SetComputedBSize(NS_UNCONSTRAINEDSIZE, ResetResizeFlags::No); + } else { + SetComputedBSize( + ComputeBSizeValue(cbSize.BSize(wm), mStylePosition->mBoxSizing, + blockSize.AsLengthPercentage()), + ResetResizeFlags::No); + } + + // Doesn't apply to internal table elements + mComputedMinSize.SizeTo(mWritingMode, 0, 0); + mComputedMaxSize.SizeTo(mWritingMode, NS_UNCONSTRAINEDSIZE, + NS_UNCONSTRAINEDSIZE); + } else if (mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && + mStyleDisplay->IsAbsolutelyPositionedStyle() && + // XXXfr hack for making frames behave properly when in overflow + // container lists, see bug 154892; need to revisit later + !mFrame->GetPrevInFlow()) { + InitAbsoluteConstraints(aPresContext, cbri, + cbSize.ConvertTo(cbri->GetWritingMode(), wm), + aFrameType); + } else { + AutoMaybeDisableFontInflation an(mFrame); + + const bool isBlockLevel = + ((!mStyleDisplay->IsInlineOutsideStyle() && + // internal table values on replaced elements behaves as inline + // https://drafts.csswg.org/css-tables-3/#table-structure + // "... it is handled instead as though the author had declared + // either 'block' (for 'table' display) or 'inline' (for all + // other values)" + !(mFlags.mIsReplaced && (mStyleDisplay->IsInnerTableStyle() || + mStyleDisplay->DisplayOutside() == + StyleDisplayOutside::TableCaption))) || + // The inner table frame always fills its outer wrapper table frame, + // even for 'inline-table'. + mFrame->IsTableFrame()) && + // XXX abs.pos. continuations treated like blocks, see comment in + // the else-if condition above. + (!mFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) || + mStyleDisplay->IsAbsolutelyPositionedStyle()); + + if (!isBlockLevel) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + + nsIFrame* alignCB = mFrame->GetParent(); + if (alignCB->IsTableWrapperFrame() && alignCB->GetParent()) { + // XXX grid-specific for now; maybe remove this check after we address + // bug 799725 + if (alignCB->GetParent()->IsGridContainerFrame()) { + alignCB = alignCB->GetParent(); + } + } + if (alignCB->IsGridContainerFrame()) { + // Shrink-wrap grid items that will be aligned (rather than stretched) + // in its inline axis. + auto inlineAxisAlignment = + wm.IsOrthogonalTo(cbwm) + ? mStylePosition->UsedAlignSelf(alignCB->Style())._0 + : mStylePosition->UsedJustifySelf(alignCB->Style())._0; + if ((inlineAxisAlignment != StyleAlignFlags::STRETCH && + inlineAxisAlignment != StyleAlignFlags::NORMAL) || + mStyleMargin->mMargin.GetIStart(wm).IsAuto() || + mStyleMargin->mMargin.GetIEnd(wm).IsAuto()) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + } else { + // Shrink-wrap blocks that are orthogonal to their container. + if (isBlockLevel && mCBReflowInput && + mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + + if (alignCB->IsFlexContainerFrame()) { + mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; + } + } + + if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { + // For orthogonal flows, where we found a parent orthogonal-limit + // for AvailableISize() in Init(), we'll use the same here as well. + cbSize.ISize(wm) = AvailableISize(); + } + + auto size = + mFrame->ComputeSize(mRenderingContext, wm, cbSize, AvailableISize(), + ComputedLogicalMargin(wm).Size(wm), + ComputedLogicalBorderPadding(wm).Size(wm), + mStyleSizeOverrides, mComputeSizeFlags); + + mComputedSize = size.mLogicalSize; + NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); + NS_ASSERTION( + ComputedBSize() == NS_UNCONSTRAINEDSIZE || ComputedBSize() >= 0, + "Bogus block-size"); + + mFlags.mIsBSizeSetByAspectRatio = + size.mAspectRatioUsage == nsIFrame::AspectRatioUsage::ToComputeBSize; + + const bool shouldCalculateBlockSideMargins = [&]() { + if (!isBlockLevel) { + return false; + } + if (mStyleDisplay->mDisplay == StyleDisplay::InlineTable) { + return false; + } + if (mFrame->IsTableFrame()) { + return false; + } + if (alignCB->IsFlexOrGridContainer()) { + // Exclude flex and grid items. + return false; + } + const auto pseudoType = mFrame->Style()->GetPseudoType(); + if (pseudoType == PseudoStyleType::marker && + mFrame->GetParent()->StyleList()->mListStylePosition == + StyleListStylePosition::Outside) { + // Exclude outside ::markers. + return false; + } + if (pseudoType == PseudoStyleType::columnContent) { + // Exclude -moz-column-content since it cannot have any margin. + return false; + } + return true; + }(); + + if (shouldCalculateBlockSideMargins) { + CalculateBlockSideMargins(); + } + } + } + + // Save our containing block dimensions + mContainingBlockSize = cbSize; +} + +static void UpdateProp(nsIFrame* aFrame, + const FramePropertyDescriptor* aProperty, + bool aNeeded, const nsMargin& aNewValue) { + if (aNeeded) { + nsMargin* propValue = aFrame->GetProperty(aProperty); + if (propValue) { + *propValue = aNewValue; + } else { + aFrame->AddProperty(aProperty, new nsMargin(aNewValue)); + } + } else { + aFrame->RemoveProperty(aProperty); + } +} + +void SizeComputationInput::InitOffsets(WritingMode aCBWM, nscoord aPercentBasis, + LayoutFrameType aFrameType, + ComputeSizeFlags aFlags, + const Maybe& aBorder, + const Maybe& aPadding, + const nsStyleDisplay* aDisplay) { + DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aCBWM, aBorder, aPadding); + + // Since we are in reflow, we don't need to store these properties anymore + // unless they are dependent on width, in which case we store the new value. + nsPresContext* presContext = mFrame->PresContext(); + mFrame->RemoveProperty(nsIFrame::UsedBorderProperty()); + + // Compute margins from the specified margin style information. These + // become the default computed values, and may be adjusted below + // XXX fix to provide 0,0 for the top&bottom margins for + // inline-non-replaced elements + bool needMarginProp = ComputeMargin(aCBWM, aPercentBasis, aFrameType); + // Note that ComputeMargin() simplistically resolves 'auto' margins to 0. + // In formatting contexts where this isn't correct, some later code will + // need to update the UsedMargin() property with the actual resolved value. + // One example of this is ::CalculateBlockSideMargins(). + ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp, + ComputedPhysicalMargin()); + + const WritingMode wm = GetWritingMode(); + const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay); + bool needPaddingProp; + LayoutDeviceIntMargin widgetPadding; + if (mIsThemed && presContext->Theme()->GetWidgetPadding( + presContext->DeviceContext(), mFrame, + disp->EffectiveAppearance(), &widgetPadding)) { + const nsMargin padding = LayoutDevicePixel::ToAppUnits( + widgetPadding, presContext->AppUnitsPerDevPixel()); + SetComputedLogicalPadding(wm, LogicalMargin(wm, padding)); + needPaddingProp = false; + } else if (mFrame->IsInSVGTextSubtree()) { + SetComputedLogicalPadding(wm, LogicalMargin(wm)); + needPaddingProp = false; + } else if (aPadding) { // padding is an input arg + SetComputedLogicalPadding(wm, *aPadding); + nsMargin stylePadding; + // If the caller passes a padding that doesn't match our style (like + // nsTextControlFrame might due due to theming), then we also need a + // padding prop. + needPaddingProp = !mFrame->StylePadding()->GetPadding(stylePadding) || + aPadding->GetPhysicalMargin(wm) != stylePadding; + } else { + needPaddingProp = ComputePadding(aCBWM, aPercentBasis, aFrameType); + } + + // Add [align|justify]-content:baseline padding contribution. + typedef const FramePropertyDescriptor>* Prop; + auto ApplyBaselinePadding = [this, wm, &needPaddingProp](LogicalAxis aAxis, + Prop aProp) { + bool found; + nscoord val = mFrame->GetProperty(aProp, &found); + if (found) { + NS_ASSERTION(val != nscoord(0), "zero in this property is useless"); + LogicalSide side; + if (val > 0) { + side = MakeLogicalSide(aAxis, eLogicalEdgeStart); + } else { + side = MakeLogicalSide(aAxis, eLogicalEdgeEnd); + val = -val; + } + mComputedPadding.Side(side, wm) += val; + needPaddingProp = true; + if (aAxis == eLogicalAxisBlock && val > 0) { + // We have a baseline-adjusted block-axis start padding, so + // we need this to mark lines dirty when mIsBResize is true: + this->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); + } + } + }; + if (!aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow)) { + ApplyBaselinePadding(eLogicalAxisBlock, nsIFrame::BBaselinePadProperty()); + } + if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap)) { + ApplyBaselinePadding(eLogicalAxisInline, nsIFrame::IBaselinePadProperty()); + } + + LogicalMargin border(wm); + if (mIsThemed) { + const LayoutDeviceIntMargin widgetBorder = + presContext->Theme()->GetWidgetBorder( + presContext->DeviceContext(), mFrame, disp->EffectiveAppearance()); + border = LogicalMargin( + wm, LayoutDevicePixel::ToAppUnits(widgetBorder, + presContext->AppUnitsPerDevPixel())); + } else if (mFrame->IsInSVGTextSubtree()) { + // Do nothing since the border local variable is initialized all zero. + } else if (aBorder) { // border is an input arg + border = *aBorder; + } else { + border = LogicalMargin(wm, mFrame->StyleBorder()->GetComputedBorder()); + } + SetComputedLogicalBorderPadding(wm, border + ComputedLogicalPadding(wm)); + + if (aFrameType == LayoutFrameType::Scrollbar) { + // scrollbars may have had their width or height smashed to zero + // by the associated scrollframe, in which case we must not report + // any padding or border. + nsSize size(mFrame->GetSize()); + if (size.width == 0 || size.height == 0) { + SetComputedLogicalPadding(wm, LogicalMargin(wm)); + SetComputedLogicalBorderPadding(wm, LogicalMargin(wm)); + } + } + + bool hasPaddingChange; + if (nsMargin* oldPadding = + mFrame->GetProperty(nsIFrame::UsedPaddingProperty())) { + // Note: If a padding change is already detectable without resolving the + // percentage, e.g. a padding is changing from 50px to 50%, + // nsIFrame::DidSetComputedStyle() will cache the old padding in + // UsedPaddingProperty(). + hasPaddingChange = *oldPadding != ComputedPhysicalPadding(); + } else { + // Our padding may have changed, but we can't tell at this point. + hasPaddingChange = needPaddingProp; + } + // Keep mHasPaddingChange bit set until we've done reflow. We'll clear it in + // nsIFrame::DidReflow() + mFrame->SetHasPaddingChange(mFrame->HasPaddingChange() || hasPaddingChange); + + ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp, + ComputedPhysicalPadding()); +} + +// This code enforces section 10.3.3 of the CSS2 spec for this formula: +// +// 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + +// 'padding-right' + 'border-right-width' + 'margin-right' +// = width of containing block +// +// Note: the width unit is not auto when this is called +void ReflowInput::CalculateBlockSideMargins() { + MOZ_ASSERT(!mFrame->IsTableFrame(), + "Inner table frame cannot have computed margins!"); + + // Calculations here are done in the containing block's writing mode, + // which is where margins will eventually be applied: we're calculating + // margins that will be used by the container in its inline direction, + // which in the case of an orthogonal contained block will correspond to + // the block direction of this reflow input. So in the orthogonal-flow + // case, "CalculateBlock*Side*Margins" will actually end up adjusting + // the BStart/BEnd margins; those are the "sides" of the block from its + // container's point of view. + WritingMode cbWM = + mCBReflowInput ? mCBReflowInput->GetWritingMode() : GetWritingMode(); + + nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM); + nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM); + if (computedISizeCBWM == NS_UNCONSTRAINEDSIZE) { + // For orthogonal flows, where we found a parent orthogonal-limit + // for AvailableISize() in Init(), we don't have meaningful sizes to + // adjust. Act like the sum is already correct (below). + return; + } + + LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM && + NS_UNCONSTRAINEDSIZE != availISizeCBWM, + "have unconstrained inline-size; this should only " + "result from very large sizes, not attempts at " + "intrinsic inline-size calculation"); + + LogicalMargin margin = ComputedLogicalMargin(cbWM); + LogicalMargin borderPadding = ComputedLogicalBorderPadding(cbWM); + nscoord sum = margin.IStartEnd(cbWM) + borderPadding.IStartEnd(cbWM) + + computedISizeCBWM; + if (sum == availISizeCBWM) { + // The sum is already correct + return; + } + + // Determine the start and end margin values. The isize value + // remains constant while we do this. + + // Calculate how much space is available for margins + nscoord availMarginSpace = availISizeCBWM - sum; + + // If the available margin space is negative, then don't follow the + // usual overconstraint rules. + if (availMarginSpace < 0) { + margin.IEnd(cbWM) += availMarginSpace; + SetComputedLogicalMargin(cbWM, margin); + return; + } + + // The css2 spec clearly defines how block elements should behave + // in section 10.3.3. + const auto& styleSides = mStyleMargin->mMargin; + bool isAutoStartMargin = styleSides.GetIStart(cbWM).IsAuto(); + bool isAutoEndMargin = styleSides.GetIEnd(cbWM).IsAuto(); + if (!isAutoStartMargin && !isAutoEndMargin) { + // Neither margin is 'auto' so we're over constrained. Use the + // 'direction' property of the parent to tell which margin to + // ignore + // First check if there is an HTML alignment that we should honor + const StyleTextAlign* textAlign = + mParentReflowInput + ? &mParentReflowInput->mFrame->StyleText()->mTextAlign + : nullptr; + if (textAlign && (*textAlign == StyleTextAlign::MozLeft || + *textAlign == StyleTextAlign::MozCenter || + *textAlign == StyleTextAlign::MozRight)) { + if (mParentReflowInput->mWritingMode.IsBidiLTR()) { + isAutoStartMargin = *textAlign != StyleTextAlign::MozLeft; + isAutoEndMargin = *textAlign != StyleTextAlign::MozRight; + } else { + isAutoStartMargin = *textAlign != StyleTextAlign::MozRight; + isAutoEndMargin = *textAlign != StyleTextAlign::MozLeft; + } + } + // Otherwise apply the CSS rules, and ignore one margin by forcing + // it to 'auto', depending on 'direction'. + else { + isAutoEndMargin = true; + } + } + + // Logic which is common to blocks and tables + // The computed margins need not be zero because the 'auto' could come from + // overconstraint or from HTML alignment so values need to be accumulated + + if (isAutoStartMargin) { + if (isAutoEndMargin) { + // Both margins are 'auto' so the computed addition should be equal + nscoord forStart = availMarginSpace / 2; + margin.IStart(cbWM) += forStart; + margin.IEnd(cbWM) += availMarginSpace - forStart; + } else { + margin.IStart(cbWM) += availMarginSpace; + } + } else if (isAutoEndMargin) { + margin.IEnd(cbWM) += availMarginSpace; + } + SetComputedLogicalMargin(cbWM, margin); + + if (isAutoStartMargin || isAutoEndMargin) { + // Update the UsedMargin property if we were tracking it already. + nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty()); + if (propValue) { + *propValue = margin.GetPhysicalMargin(cbWM); + } + } +} + +// For "normal" we use the font's normal line height (em height + leading). +// If both internal leading and external leading specified by font itself are +// zeros, we should compensate this by creating extra (external) leading. +// This is necessary because without this compensation, normal line height might +// look too tight. +constexpr float kNormalLineHeightFactor = 1.2f; +static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) { + MOZ_ASSERT(aFontMetrics, "no font metrics"); + nscoord externalLeading = aFontMetrics->ExternalLeading(); + nscoord internalLeading = aFontMetrics->InternalLeading(); + nscoord emHeight = aFontMetrics->EmHeight(); + if (!internalLeading && !externalLeading) { + return NSToCoordRound(emHeight * kNormalLineHeightFactor); + } + return emHeight + internalLeading + externalLeading; +} + +static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh, + const nsStyleFont& aRelativeToFont, + nsPresContext* aPresContext, + bool aIsVertical, nscoord aBlockBSize, + float aFontSizeInflation) { + if (aLh.IsLength()) { + nscoord result = aLh.AsLength().ToAppUnits(); + if (aFontSizeInflation != 1.0f) { + result = NSToCoordRound(result * aFontSizeInflation); + } + return result; + } + + if (aLh.IsNumber()) { + // For factor units the computed value of the line-height property + // is found by multiplying the factor by the font's computed size + // (adjusted for min-size prefs and text zoom). + return aRelativeToFont.mFont.size + .ScaledBy(aLh.AsNumber() * aFontSizeInflation) + .ToAppUnits(); + } + + MOZ_ASSERT(aLh.IsNormal() || aLh.IsMozBlockHeight()); + if (aLh.IsMozBlockHeight() && aBlockBSize != NS_UNCONSTRAINEDSIZE) { + return aBlockBSize; + } + + auto size = aRelativeToFont.mFont.size; + size.ScaleBy(aFontSizeInflation); + + if (aPresContext) { + RefPtr fm = nsLayoutUtils::GetMetricsFor( + aPresContext, aIsVertical, &aRelativeToFont, size, + /* aUseUserFontSet = */ true); + return GetNormalLineHeight(fm); + } + // If we don't have a pres context, use a 1.2em fallback. + size.ScaleBy(kNormalLineHeightFactor); + return size.ToAppUnits(); +} + +nscoord ReflowInput::GetLineHeight() const { + if (mLineHeight != NS_UNCONSTRAINEDSIZE) { + return mLineHeight; + } + + nscoord blockBSize = nsLayoutUtils::IsNonWrapperBlock(mFrame) + ? ComputedBSize() + : (mCBReflowInput ? mCBReflowInput->ComputedBSize() + : NS_UNCONSTRAINEDSIZE); + mLineHeight = CalcLineHeight(*mFrame->Style(), mFrame->PresContext(), + mFrame->GetContent(), blockBSize, + nsLayoutUtils::FontSizeInflationFor(mFrame)); + return mLineHeight; +} + +void ReflowInput::SetLineHeight(nscoord aLineHeight) { + MOZ_ASSERT(aLineHeight >= 0, "aLineHeight must be >= 0!"); + + if (mLineHeight != aLineHeight) { + mLineHeight = aLineHeight; + // Setting used line height can change a frame's block-size if mFrame's + // block-size behaves as auto. + InitResizeFlags(mFrame->PresContext(), mFrame->Type()); + } +} + +/* static */ +nscoord ReflowInput::CalcLineHeight(const ComputedStyle& aStyle, + nsPresContext* aPresContext, + const nsIContent* aContent, + nscoord aBlockBSize, + float aFontSizeInflation) { + const StyleLineHeight& lh = aStyle.StyleText()->mLineHeight; + WritingMode wm(&aStyle); + const bool vertical = wm.IsVertical() && !wm.IsSideways(); + return CalcLineHeight(lh, *aStyle.StyleFont(), aPresContext, vertical, + aContent, aBlockBSize, aFontSizeInflation); +} + +nscoord ReflowInput::CalcLineHeight( + const StyleLineHeight& aLh, const nsStyleFont& aRelativeToFont, + nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent, + nscoord aBlockBSize, float aFontSizeInflation) { + nscoord lineHeight = + ComputeLineHeight(aLh, aRelativeToFont, aPresContext, aIsVertical, + aBlockBSize, aFontSizeInflation); + + NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); + + const auto* input = HTMLInputElement::FromNodeOrNull(aContent); + if (input && input->IsSingleLineTextControl()) { + // For Web-compatibility, single-line text input elements cannot + // have a line-height smaller than 'normal'. + if (!aLh.IsNormal()) { + nscoord normal = ComputeLineHeight( + StyleLineHeight::Normal(), aRelativeToFont, aPresContext, aIsVertical, + aBlockBSize, aFontSizeInflation); + if (lineHeight < normal) { + lineHeight = normal; + } + } + } + + return lineHeight; +} + +bool SizeComputationInput::ComputeMargin(WritingMode aCBWM, + nscoord aPercentBasis, + LayoutFrameType aFrameType) { + // SVG text frames have no margin. + if (mFrame->IsInSVGTextSubtree()) { + return false; + } + + if (aFrameType == LayoutFrameType::Table) { + // Table frame's margin is inherited to the table wrapper frame via the + // ::-moz-table-wrapper rule in ua.css, so don't set any margins for it. + SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode)); + return false; + } + + // If style style can provide us the margin directly, then use it. + const nsStyleMargin* styleMargin = mFrame->StyleMargin(); + + nsMargin margin; + const bool isCBDependent = !styleMargin->GetMargin(margin); + if (isCBDependent) { + // We have to compute the value. Note that this calculation is + // performed according to the writing mode of the containing block + // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) + if (aPercentBasis == NS_UNCONSTRAINEDSIZE) { + aPercentBasis = 0; + } + LogicalMargin m(aCBWM); + m.IStart(aCBWM) = nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, styleMargin->mMargin.GetIStart(aCBWM)); + m.IEnd(aCBWM) = nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, styleMargin->mMargin.GetIEnd(aCBWM)); + + m.BStart(aCBWM) = nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, styleMargin->mMargin.GetBStart(aCBWM)); + m.BEnd(aCBWM) = nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, styleMargin->mMargin.GetBEnd(aCBWM)); + + SetComputedLogicalMargin(aCBWM, m); + } else { + SetComputedLogicalMargin(mWritingMode, LogicalMargin(mWritingMode, margin)); + } + + // ... but font-size-inflation-based margin adjustment uses the + // frame's writing mode + nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame); + + if (marginAdjustment > 0) { + LogicalMargin m = ComputedLogicalMargin(mWritingMode); + m.IStart(mWritingMode) += marginAdjustment; + SetComputedLogicalMargin(mWritingMode, m); + } + + return isCBDependent; +} + +bool SizeComputationInput::ComputePadding(WritingMode aCBWM, + nscoord aPercentBasis, + LayoutFrameType aFrameType) { + // If style can provide us the padding directly, then use it. + const nsStylePadding* stylePadding = mFrame->StylePadding(); + nsMargin padding; + bool isCBDependent = !stylePadding->GetPadding(padding); + // a table row/col group, row/col doesn't have padding + // XXXldb Neither do border-collapse tables. + if (LayoutFrameType::TableRowGroup == aFrameType || + LayoutFrameType::TableColGroup == aFrameType || + LayoutFrameType::TableRow == aFrameType || + LayoutFrameType::TableCol == aFrameType) { + SetComputedLogicalPadding(mWritingMode, LogicalMargin(mWritingMode)); + } else if (isCBDependent) { + // We have to compute the value. This calculation is performed + // according to the writing mode of the containing block + // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) + // clamp negative calc() results to 0 + if (aPercentBasis == NS_UNCONSTRAINEDSIZE) { + aPercentBasis = 0; + } + LogicalMargin p(aCBWM); + p.IStart(aCBWM) = std::max( + 0, nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, stylePadding->mPadding.GetIStart(aCBWM))); + p.IEnd(aCBWM) = + std::max(0, nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, stylePadding->mPadding.GetIEnd(aCBWM))); + + p.BStart(aCBWM) = std::max( + 0, nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, stylePadding->mPadding.GetBStart(aCBWM))); + p.BEnd(aCBWM) = + std::max(0, nsLayoutUtils::ComputeCBDependentValue( + aPercentBasis, stylePadding->mPadding.GetBEnd(aCBWM))); + + SetComputedLogicalPadding(aCBWM, p); + } else { + SetComputedLogicalPadding(mWritingMode, + LogicalMargin(mWritingMode, padding)); + } + return isCBDependent; +} + +void ReflowInput::ComputeMinMaxValues(const LogicalSize& aCBSize) { + WritingMode wm = GetWritingMode(); + + const auto& minISize = mStylePosition->MinISize(wm); + const auto& maxISize = mStylePosition->MaxISize(wm); + const auto& minBSize = mStylePosition->MinBSize(wm); + const auto& maxBSize = mStylePosition->MaxBSize(wm); + + LogicalSize minWidgetSize(wm); + if (mIsThemed) { + nsPresContext* pc = mFrame->PresContext(); + const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize( + pc, mFrame, mStyleDisplay->EffectiveAppearance()); + + // Convert themed widget's physical dimensions to logical coords. + minWidgetSize = { + wm, LayoutDeviceIntSize::ToAppUnits(widget, pc->AppUnitsPerDevPixel())}; + + // GetMinimumWidgetSize() returns border-box; we need content-box. + minWidgetSize -= ComputedLogicalBorderPadding(wm).Size(wm); + } + + // NOTE: min-width:auto resolves to 0, except on a flex item. (But + // even there, it's supposed to be ignored (i.e. treated as 0) until + // the flex container explicitly resolves & considers it.) + if (minISize.IsAuto()) { + SetComputedMinISize(0); + } else { + SetComputedMinISize( + ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, minISize)); + } + + if (mIsThemed) { + SetComputedMinISize(std::max(ComputedMinISize(), minWidgetSize.ISize(wm))); + } + + if (maxISize.IsNone()) { + // Specified value of 'none' + SetComputedMaxISize(NS_UNCONSTRAINEDSIZE); + } else { + SetComputedMaxISize( + ComputeISizeValue(aCBSize, mStylePosition->mBoxSizing, maxISize)); + } + + // If the computed value of 'min-width' is greater than the value of + // 'max-width', 'max-width' is set to the value of 'min-width' + if (ComputedMinISize() > ComputedMaxISize()) { + SetComputedMaxISize(ComputedMinISize()); + } + + // Check for percentage based values and a containing block height that + // depends on the content height. Treat them like the initial value. + // Likewise, check for calc() with percentages on internal table elements; + // that's treated as the initial value too. + const bool isInternalTableFrame = IsInternalTableFrame(); + const nscoord& bPercentageBasis = aCBSize.BSize(wm); + auto BSizeBehavesAsInitialValue = [&](const auto& aBSize) { + if (nsLayoutUtils::IsAutoBSize(aBSize, bPercentageBasis)) { + return true; + } + if (isInternalTableFrame) { + return aBSize.HasLengthAndPercentage(); + } + return false; + }; + + // NOTE: min-height:auto resolves to 0, except on a flex item. (But + // even there, it's supposed to be ignored (i.e. treated as 0) until + // the flex container explicitly resolves & considers it.) + if (BSizeBehavesAsInitialValue(minBSize)) { + SetComputedMinBSize(0); + } else { + SetComputedMinBSize(ComputeBSizeValue(bPercentageBasis, + mStylePosition->mBoxSizing, + minBSize.AsLengthPercentage())); + } + + if (mIsThemed) { + SetComputedMinBSize(std::max(ComputedMinBSize(), minWidgetSize.BSize(wm))); + } + + if (BSizeBehavesAsInitialValue(maxBSize)) { + // Specified value of 'none' + SetComputedMaxBSize(NS_UNCONSTRAINEDSIZE); + } else { + SetComputedMaxBSize(ComputeBSizeValue(bPercentageBasis, + mStylePosition->mBoxSizing, + maxBSize.AsLengthPercentage())); + } + + // If the computed value of 'min-height' is greater than the value of + // 'max-height', 'max-height' is set to the value of 'min-height' + if (ComputedMinBSize() > ComputedMaxBSize()) { + SetComputedMaxBSize(ComputedMinBSize()); + } +} + +bool ReflowInput::IsInternalTableFrame() const { + return mFrame->IsTableRowGroupFrame() || mFrame->IsTableColGroupFrame() || + mFrame->IsTableRowFrame() || mFrame->IsTableCellFrame(); +} diff --git a/layout/generic/ReflowInput.h b/layout/generic/ReflowInput.h new file mode 100644 index 0000000000..370ccc1af2 --- /dev/null +++ b/layout/generic/ReflowInput.h @@ -0,0 +1,1062 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* struct containing the input to nsIFrame::Reflow */ + +#ifndef mozilla_ReflowInput_h +#define mozilla_ReflowInput_h + +#include "nsMargin.h" +#include "nsStyleConsts.h" +#include "mozilla/Assertions.h" +#include "mozilla/EnumSet.h" +#include "mozilla/Maybe.h" +#include "mozilla/WritingModes.h" +#include "LayoutConstants.h" +#include "ReflowOutput.h" +#include + +class gfxContext; +class nsFloatManager; +struct nsHypotheticalPosition; +class nsIPercentBSizeObserver; +class nsLineLayout; +class nsPlaceholderFrame; +class nsPresContext; +class nsReflowStatus; + +namespace mozilla { +enum class LayoutFrameType : uint8_t; + +/** + * A set of StyleSizes used as an input parameter to various functions that + * compute sizes like nsIFrame::ComputeSize(). If any of the member fields has a + * value, the function may use the value instead of retrieving it from the + * frame's style. + * + * The logical sizes are assumed to be in the associated frame's writing-mode. + */ +struct StyleSizeOverrides { + Maybe mStyleISize; + Maybe mStyleBSize; + Maybe mAspectRatio; + + bool HasAnyOverrides() const { return mStyleISize || mStyleBSize; } + bool HasAnyLengthOverrides() const { + return (mStyleISize && mStyleISize->ConvertsToLength()) || + (mStyleBSize && mStyleBSize->ConvertsToLength()); + } + + // By default, table wrapper frame considers the size overrides applied to + // itself, so it creates any length size overrides for inner table frame by + // subtracting the area occupied by the caption and border & padding according + // to box-sizing. + // + // When this flag is true, table wrapper frame is required to apply the size + // overrides to the inner table frame directly, without any modification, + // which is useful for flex container to override the inner table frame's + // preferred main size with 'flex-basis'. + // + // Note: if mStyleISize is a LengthPercentage, the inner table frame will + // comply with the inline-size override without enforcing its min-content + // inline-size in nsTableFrame::ComputeSize(). This is necessary so that small + // flex-basis values like 'flex-basis:1%' can be resolved correctly; the + // flexbox layout algorithm does still explicitly clamp to min-sizes *at a + // later step*, after the flex-basis has been resolved -- so this flag won't + // actually produce any user-visible tables whose final inline size is smaller + // than their min-content inline size. + bool mApplyOverridesVerbatim = false; +}; +} // namespace mozilla + +/** + * @return aValue clamped to [aMinValue, aMaxValue]. + * + * @note This function needs to handle aMinValue > aMaxValue. In that case, + * aMinValue is returned. That's why we cannot use std::clamp() and + * mozilla::clamped() since they both assert max >= min. + * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-widths + * @see http://www.w3.org/TR/CSS21/visudet.html#min-max-heights + */ +template +NumericType NS_CSS_MINMAX(NumericType aValue, NumericType aMinValue, + NumericType aMaxValue) { + NumericType result = aValue; + if (aMaxValue < result) result = aMaxValue; + if (aMinValue > result) result = aMinValue; + return result; +} + +namespace mozilla { + +// A base class of ReflowInput that computes only the padding, +// border, and margin, since those values are needed more often. +struct SizeComputationInput { + public: + // The frame being reflowed. + nsIFrame* const mFrame; + + // Rendering context to use for measurement. + gfxContext* mRenderingContext; + + nsMargin ComputedPhysicalMargin() const { + return mComputedMargin.GetPhysicalMargin(mWritingMode); + } + nsMargin ComputedPhysicalBorderPadding() const { + return mComputedBorderPadding.GetPhysicalMargin(mWritingMode); + } + nsMargin ComputedPhysicalPadding() const { + return mComputedPadding.GetPhysicalMargin(mWritingMode); + } + + mozilla::LogicalMargin ComputedLogicalMargin(mozilla::WritingMode aWM) const { + return mComputedMargin.ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalMargin ComputedLogicalBorderPadding( + mozilla::WritingMode aWM) const { + return mComputedBorderPadding.ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalMargin ComputedLogicalPadding( + mozilla::WritingMode aWM) const { + return mComputedPadding.ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalMargin ComputedLogicalBorder(mozilla::WritingMode aWM) const { + return (mComputedBorderPadding - mComputedPadding) + .ConvertTo(aWM, mWritingMode); + } + + void SetComputedLogicalMargin(mozilla::WritingMode aWM, + const mozilla::LogicalMargin& aMargin) { + mComputedMargin = aMargin.ConvertTo(mWritingMode, aWM); + } + void SetComputedLogicalBorderPadding( + mozilla::WritingMode aWM, const mozilla::LogicalMargin& aBorderPadding) { + mComputedBorderPadding = aBorderPadding.ConvertTo(mWritingMode, aWM); + } + void SetComputedLogicalPadding(mozilla::WritingMode aWM, + const mozilla::LogicalMargin& aPadding) { + mComputedPadding = aPadding.ConvertTo(mWritingMode, aWM); + } + + mozilla::WritingMode GetWritingMode() const { return mWritingMode; } + + protected: + // cached copy of the frame's writing-mode, for logical coordinates + const mozilla::WritingMode mWritingMode; + + // Cached mFrame->IsThemed(). + const bool mIsThemed = false; + + // Computed margin values + mozilla::LogicalMargin mComputedMargin; + + // Cached copy of the border + padding values + mozilla::LogicalMargin mComputedBorderPadding; + + // Computed padding values + mozilla::LogicalMargin mComputedPadding; + + public: + // Callers using this constructor must call InitOffsets on their own. + SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext); + + SizeComputationInput(nsIFrame* aFrame, gfxContext* aRenderingContext, + mozilla::WritingMode aContainingBlockWritingMode, + nscoord aContainingBlockISize, + const mozilla::Maybe& aBorder = + mozilla::Nothing(), + const mozilla::Maybe& aPadding = + mozilla::Nothing()); + +#ifdef DEBUG + // Reflow trace methods. Defined in nsFrame.cpp so they have access + // to the display-reflow infrastructure. + static void* DisplayInitOffsetsEnter(nsIFrame* aFrame, + SizeComputationInput* aState, + nscoord aPercentBasis, + mozilla::WritingMode aCBWritingMode, + const nsMargin* aBorder, + const nsMargin* aPadding); + static void DisplayInitOffsetsExit(nsIFrame* aFrame, + SizeComputationInput* aState, + void* aValue); +#endif + + private: + /** + * Computes margin values from the specified margin style information, and + * fills in the mComputedMargin member. + * + * @param aCBWM Writing mode of the containing block + * @param aPercentBasis + * Inline size of the containing block (in its own writing mode), to use + * for resolving percentage margin values in the inline and block axes. + * @return true if the margin is dependent on the containing block size. + */ + bool ComputeMargin(mozilla::WritingMode aCBWM, nscoord aPercentBasis, + mozilla::LayoutFrameType aFrameType); + + /** + * Computes padding values from the specified padding style information, and + * fills in the mComputedPadding member. + * + * @param aCBWM Writing mode of the containing block + * @param aPercentBasis + * Inline size of the containing block (in its own writing mode), to use + * for resolving percentage padding values in the inline and block axes. + * @return true if the padding is dependent on the containing block size. + */ + bool ComputePadding(mozilla::WritingMode aCBWM, nscoord aPercentBasis, + mozilla::LayoutFrameType aFrameType); + + protected: + void InitOffsets(mozilla::WritingMode aCBWM, nscoord aPercentBasis, + mozilla::LayoutFrameType aFrameType, + mozilla::ComputeSizeFlags aFlags, + const mozilla::Maybe& aBorder, + const mozilla::Maybe& aPadding, + const nsStyleDisplay* aDisplay = nullptr); + + /* + * Convert StyleSize or StyleMaxSize to nscoord when percentages depend on the + * inline size of the containing block, and enumerated values are for inline + * size, min-inline-size, or max-inline-size. Does not handle auto inline + * sizes. + */ + template + inline nscoord ComputeISizeValue(const WritingMode aWM, + const LogicalSize& aContainingBlockSize, + const LogicalSize& aContentEdgeToBoxSizing, + nscoord aBoxSizingToMarginEdge, + const SizeOrMaxSize&) const; + // same as previous, but using mComputedBorderPadding, mComputedPadding, + // and mComputedMargin + template + inline nscoord ComputeISizeValue(const LogicalSize& aContainingBlockSize, + mozilla::StyleBoxSizing aBoxSizing, + const SizeOrMaxSize&) const; + + nscoord ComputeBSizeValue(nscoord aContainingBlockBSize, + mozilla::StyleBoxSizing aBoxSizing, + const mozilla::LengthPercentage& aCoord) const; +}; + +/** + * State passed to a frame during reflow or intrinsic size calculation. + * + * XXX Refactor so only a base class (nsSizingState?) is used for intrinsic + * size calculation. + * + * @see nsIFrame#Reflow() + */ +struct ReflowInput : public SizeComputationInput { + // the reflow inputs are linked together. this is the pointer to the + // parent's reflow input + const ReflowInput* mParentReflowInput = nullptr; + + // A non-owning pointer to the float manager associated with this area, + // which points to the object owned by nsAutoFloatManager::mNew. + nsFloatManager* mFloatManager = nullptr; + + // LineLayout object (only for inline reflow; set to nullptr otherwise) + nsLineLayout* mLineLayout = nullptr; + + // The appropriate reflow input for the containing block (for + // percentage widths, etc.) of this reflow input's frame. It will be setup + // properly in InitCBReflowInput(). + const ReflowInput* mCBReflowInput = nullptr; + + // The amount the in-flow position of the block is moving vertically relative + // to its previous in-flow position (i.e. the amount the line containing the + // block is moving). + // This should be zero for anything which is not a block outside, and it + // should be zero for anything which has a non-block parent. + // The intended use of this value is to allow the accurate determination + // of the potential impact of a float + // This takes on an arbitrary value the first time a block is reflowed + nscoord mBlockDelta = 0; + + // If a ReflowInput finds itself initialized with an unconstrained + // inline-size, it will look up its parentReflowInput chain for a reflow input + // with an orthogonal writing mode and a non-NS_UNCONSTRAINEDSIZE value for + // orthogonal limit; when it finds such a reflow input, it will use its + // orthogonal-limit value to constrain inline-size. + // This is initialized to NS_UNCONSTRAINEDSIZE (so it will be ignored), + // but reset to a suitable value for the reflow root by PresShell. + nscoord mOrthogonalLimit = NS_UNCONSTRAINEDSIZE; + + // Physical accessors for the private fields. They are needed for + // compatibility with not-yet-updated code. New code should use the accessors + // for logical coordinates, unless the code really works on physical + // coordinates. + nscoord AvailableWidth() const { return mAvailableSize.Width(mWritingMode); } + nscoord AvailableHeight() const { + return mAvailableSize.Height(mWritingMode); + } + nscoord ComputedWidth() const { return mComputedSize.Width(mWritingMode); } + nscoord ComputedHeight() const { return mComputedSize.Height(mWritingMode); } + nscoord ComputedMinWidth() const { + return mComputedMinSize.Width(mWritingMode); + } + nscoord ComputedMaxWidth() const { + return mComputedMaxSize.Width(mWritingMode); + } + nscoord ComputedMinHeight() const { + return mComputedMinSize.Height(mWritingMode); + } + nscoord ComputedMaxHeight() const { + return mComputedMaxSize.Height(mWritingMode); + } + + // Logical accessors for private fields in mWritingMode. + nscoord AvailableISize() const { return mAvailableSize.ISize(mWritingMode); } + nscoord AvailableBSize() const { return mAvailableSize.BSize(mWritingMode); } + nscoord ComputedISize() const { return mComputedSize.ISize(mWritingMode); } + nscoord ComputedBSize() const { return mComputedSize.BSize(mWritingMode); } + nscoord ComputedMinISize() const { + return mComputedMinSize.ISize(mWritingMode); + } + nscoord ComputedMaxISize() const { + return mComputedMaxSize.ISize(mWritingMode); + } + nscoord ComputedMinBSize() const { + return mComputedMinSize.BSize(mWritingMode); + } + nscoord ComputedMaxBSize() const { + return mComputedMaxSize.BSize(mWritingMode); + } + + // WARNING: In general, adjusting available inline-size or block-size is not + // safe because ReflowInput has members whose values depend on the available + // size passing through the constructor. For example, + // CalculateBlockSideMargins() is called during initialization, and uses + // AvailableSize(). Make sure your use case doesn't lead to stale member + // values in ReflowInput! + void SetAvailableISize(nscoord aAvailableISize) { + mAvailableSize.ISize(mWritingMode) = aAvailableISize; + } + void SetAvailableBSize(nscoord aAvailableBSize) { + mAvailableSize.BSize(mWritingMode) = aAvailableBSize; + } + + void SetComputedMinISize(nscoord aMinISize) { + mComputedMinSize.ISize(mWritingMode) = aMinISize; + } + void SetComputedMaxISize(nscoord aMaxISize) { + mComputedMaxSize.ISize(mWritingMode) = aMaxISize; + } + void SetComputedMinBSize(nscoord aMinBSize) { + mComputedMinSize.BSize(mWritingMode) = aMinBSize; + } + void SetComputedMaxBSize(nscoord aMaxBSize) { + mComputedMaxSize.BSize(mWritingMode) = aMaxBSize; + } + + mozilla::LogicalSize AvailableSize() const { return mAvailableSize; } + mozilla::LogicalSize ComputedSize() const { return mComputedSize; } + mozilla::LogicalSize ComputedMinSize() const { return mComputedMinSize; } + mozilla::LogicalSize ComputedMaxSize() const { return mComputedMaxSize; } + + mozilla::LogicalSize AvailableSize(mozilla::WritingMode aWM) const { + return AvailableSize().ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalSize ComputedSize(mozilla::WritingMode aWM) const { + return ComputedSize().ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalSize ComputedMinSize(mozilla::WritingMode aWM) const { + return ComputedMinSize().ConvertTo(aWM, mWritingMode); + } + mozilla::LogicalSize ComputedMaxSize(mozilla::WritingMode aWM) const { + return ComputedMaxSize().ConvertTo(aWM, mWritingMode); + } + + mozilla::LogicalSize ComputedSizeWithPadding(mozilla::WritingMode aWM) const { + return ComputedSize(aWM) + ComputedLogicalPadding(aWM).Size(aWM); + } + + mozilla::LogicalSize ComputedSizeWithBorderPadding( + mozilla::WritingMode aWM) const { + return ComputedSize(aWM) + ComputedLogicalBorderPadding(aWM).Size(aWM); + } + + mozilla::LogicalSize ComputedSizeWithMarginBorderPadding( + mozilla::WritingMode aWM) const { + return ComputedSizeWithBorderPadding(aWM) + + ComputedLogicalMargin(aWM).Size(aWM); + } + + nsSize ComputedPhysicalSize() const { + return nsSize(ComputedWidth(), ComputedHeight()); + } + + nsMargin ComputedPhysicalOffsets() const { + return mComputedOffsets.GetPhysicalMargin(mWritingMode); + } + + LogicalMargin ComputedLogicalOffsets(mozilla::WritingMode aWM) const { + return mComputedOffsets.ConvertTo(aWM, mWritingMode); + } + + void SetComputedLogicalOffsets(mozilla::WritingMode aWM, + const LogicalMargin& aOffsets) { + mComputedOffsets = aOffsets.ConvertTo(mWritingMode, aWM); + } + + // Return ReflowInput's computed size including border-padding, with + // unconstrained dimensions replaced by zero. + nsSize ComputedSizeAsContainerIfConstrained() const { + const nscoord wd = ComputedWidth(); + const nscoord ht = ComputedHeight(); + return nsSize(wd == NS_UNCONSTRAINEDSIZE + ? 0 + : wd + ComputedPhysicalBorderPadding().LeftRight(), + ht == NS_UNCONSTRAINEDSIZE + ? 0 + : ht + ComputedPhysicalBorderPadding().TopBottom()); + } + + // Our saved containing block dimensions. + LogicalSize mContainingBlockSize{mWritingMode}; + + // Cached pointers to the various style structs used during initialization. + const nsStyleDisplay* mStyleDisplay = nullptr; + const nsStylePosition* mStylePosition = nullptr; + const nsStyleBorder* mStyleBorder = nullptr; + const nsStyleMargin* mStyleMargin = nullptr; + + enum class BreakType : uint8_t { + Auto, + Column, + Page, + }; + BreakType mBreakType = BreakType::Auto; + + // a frame (e.g. nsTableCellFrame) which may need to generate a special + // reflow for percent bsize calculations + nsIPercentBSizeObserver* mPercentBSizeObserver = nullptr; + + // CSS margin collapsing sometimes requires us to reflow + // optimistically assuming that margins collapse to see if clearance + // is required. When we discover that clearance is required, we + // store the frame in which clearance was discovered to the location + // requested here. + nsIFrame** mDiscoveredClearance = nullptr; + + struct Flags { + Flags() { memset(this, 0, sizeof(*this)); } + + // cached mFrame->IsFrameOfType(nsIFrame::eReplaced) || + // mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock) + bool mIsReplaced : 1; + + // used by tables to communicate special reflow (in process) to handle + // percent bsize frames inside cells which may not have computed bsizes + bool mSpecialBSizeReflow : 1; + + // nothing in the frame's next-in-flow (or its descendants) is changing + bool mNextInFlowUntouched : 1; + + // Is the current context at the top of a page? When true, we force + // something that's too tall for a page/column to fit anyway to avoid + // infinite loops. + bool mIsTopOfPage : 1; + + // parent frame is an nsIScrollableFrame and it is assuming a horizontal + // scrollbar + bool mAssumingHScrollbar : 1; + + // parent frame is an nsIScrollableFrame and it is assuming a vertical + // scrollbar + bool mAssumingVScrollbar : 1; + + // Is frame a different inline-size than before? + bool mIsIResize : 1; + + // Is frame (potentially) a different block-size than before? + // This includes cases where the block-size is 'auto' and the + // contents or width have changed. + bool mIsBResize : 1; + + // Has this frame changed block-size in a way that affects + // block-size percentages on frames for which it is the containing + // block? This includes a change between 'auto' and a length that + // doesn't actually change the frame's block-size. It does not + // include cases where the block-size is 'auto' and the frame's + // contents have changed. + // + // In the current code, this is only true when mIsBResize is also + // true, although it doesn't necessarily need to be that way (e.g., + // in the case of a frame changing from 'auto' to a length that + // produces the same height). + bool mIsBResizeForPercentages : 1; + + // tables are splittable, this should happen only inside a page and never + // insider a column frame + bool mTableIsSplittable : 1; + + // Does frame height depend on an ancestor table-cell? + bool mHeightDependsOnAncestorCell : 1; + + // nsColumnSetFrame is balancing columns + bool mIsColumnBalancing : 1; + + // We have an ancestor nsColumnSetFrame performing the last column balancing + // reflow. The available block-size of the last column might become + // unconstrained. + bool mIsInLastColumnBalancingReflow : 1; + + // True if ColumnSetWrapperFrame has a constrained block-size, and is going + // to consume all of its block-size in this fragment. This bit is passed to + // nsColumnSetFrame to determine whether to give up balancing and create + // overflow columns. + bool mColumnSetWrapperHasNoBSizeLeft : 1; + + // If this flag is set, the BSize of this frame should be considered + // indefinite for the purposes of percent resolution on child frames (we + // should behave as if ComputedBSize() were NS_UNCONSTRAINEDSIZE when doing + // percent resolution against this.ComputedBSize()). For example: flex + // items may have their ComputedBSize() resolved ahead-of-time by their + // flex container, and yet their BSize might have to be considered + // indefinite per https://drafts.csswg.org/css-flexbox/#definite-sizes + bool mTreatBSizeAsIndefinite : 1; + + // a "fake" reflow input made in order to be the parent of a real one + bool mDummyParentReflowInput : 1; + + // Should this frame reflow its place-holder children? If the available + // height of this frame didn't change, but its in a paginated environment + // (e.g. columns), it should always reflow its placeholder children. + bool mMustReflowPlaceholders : 1; + + // the STATIC_POS_IS_CB_ORIGIN ctor flag + bool mStaticPosIsCBOrigin : 1; + + // If set, the following two flags indicate that: + // (1) this frame is absolutely-positioned (or fixed-positioned). + // (2) this frame's static position depends on the CSS Box Alignment. + // (3) we do need to compute the static position, because the frame's + // {Inline and/or Block} offsets actually depend on it. + // When these bits are set, the offset values (IStart/IEnd, BStart/BEnd) + // represent the "start" edge of the frame's CSS Box Alignment container + // area, in that axis -- and these offsets need to be further-resolved + // (with CSS Box Alignment) after we know the OOF frame's size. + // NOTE: The "I" and "B" (for "Inline" and "Block") refer the axes of the + // *containing block's writing-mode*, NOT mFrame's own writing-mode. This + // is purely for convenience, since that's the writing-mode we're dealing + // with when we set & react to these bits. + bool mIOffsetsNeedCSSAlign : 1; + bool mBOffsetsNeedCSSAlign : 1; + + // Is this frame or one of its ancestors being reflowed in a different + // continuation than the one in which it was previously reflowed? In + // other words, has it moved to a different column or page than it was in + // the previous reflow? + // + // FIXME: For now, we only ensure that this is set correctly for blocks. + // This is okay because the only thing that uses it only cares about + // whether there's been a fragment change within the same block formatting + // context. + bool mMovedBlockFragments : 1; + + // Is the block-size computed by aspect-ratio and inline size (i.e. block + // axis is the ratio-dependent axis)? We set this flag so that we can check + // whether to apply automatic content-based minimum sizes once we know the + // children's block-size (after reflowing them). + // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum + bool mIsBSizeSetByAspectRatio : 1; + + // If true, then children of this frame can generate class A breakpoints + // for paginated reflow. + bool mCanHaveClassABreakpoints : 1; + }; + Flags mFlags; + + mozilla::StyleSizeOverrides mStyleSizeOverrides; + + mozilla::ComputeSizeFlags mComputeSizeFlags; + + // This value keeps track of how deeply nested a given reflow input + // is from the top of the frame tree. + int16_t mReflowDepth = 0; + + // Logical and physical accessors for the resize flags. + bool IsHResize() const { + return mWritingMode.IsVertical() ? mFlags.mIsBResize : mFlags.mIsIResize; + } + bool IsVResize() const { + return mWritingMode.IsVertical() ? mFlags.mIsIResize : mFlags.mIsBResize; + } + bool IsIResize() const { return mFlags.mIsIResize; } + bool IsBResize() const { return mFlags.mIsBResize; } + bool IsBResizeForWM(mozilla::WritingMode aWM) const { + return aWM.IsOrthogonalTo(mWritingMode) ? mFlags.mIsIResize + : mFlags.mIsBResize; + } + bool IsBResizeForPercentagesForWM(mozilla::WritingMode aWM) const { + // This uses the relatively-accurate mIsBResizeForPercentages flag + // when the writing modes are parallel, and is a bit more + // pessimistic when orthogonal. + return !aWM.IsOrthogonalTo(mWritingMode) ? mFlags.mIsBResizeForPercentages + : IsIResize(); + } + void SetHResize(bool aValue) { + if (mWritingMode.IsVertical()) { + mFlags.mIsBResize = aValue; + } else { + mFlags.mIsIResize = aValue; + } + } + void SetVResize(bool aValue) { + if (mWritingMode.IsVertical()) { + mFlags.mIsIResize = aValue; + } else { + mFlags.mIsBResize = aValue; + } + } + void SetIResize(bool aValue) { mFlags.mIsIResize = aValue; } + void SetBResize(bool aValue) { mFlags.mIsBResize = aValue; } + + // Values for |aFlags| passed to constructor + enum class InitFlag : uint8_t { + // Indicates that the parent of this reflow input is "fake" (see + // mDummyParentReflowInput in mFlags). + DummyParentReflowInput, + + // Indicates that the calling function will initialize the reflow input, and + // that the constructor should not call Init(). + CallerWillInit, + + // The caller wants the abs.pos. static-position resolved at the origin of + // the containing block, i.e. at LogicalPoint(0, 0). (Note that this + // doesn't necessarily mean that (0, 0) is the *correct* static position + // for the frame in question.) + // @note In a Grid container's masonry axis we'll always use + // the placeholder's position in that axis regardless of this flag. + StaticPosIsCBOrigin, + }; + using InitFlags = mozilla::EnumSet; + + // Note: The copy constructor is written by the compiler automatically. You + // can use that and then override specific values if you want, or you can + // call Init as desired... + + /** + * Initialize a ROOT reflow input. + * + * @param aPresContext Must be equal to aFrame->PresContext(). + * @param aFrame The frame for whose reflow input is being constructed. + * @param aRenderingContext The rendering context to be used for measurements. + * @param aAvailableSpace The available space to reflow aFrame (in aFrame's + * writing-mode). See comments for mAvailableSize for more information. + * @param aFlags A set of flags used for additional boolean parameters (see + * InitFlags above). + */ + ReflowInput(nsPresContext* aPresContext, nsIFrame* aFrame, + gfxContext* aRenderingContext, + const mozilla::LogicalSize& aAvailableSpace, + InitFlags aFlags = {}); + + /** + * Initialize a reflow input for a child frame's reflow. Some parts of the + * state are copied from the parent's reflow input. The remainder is computed. + * + * @param aPresContext Must be equal to aFrame->PresContext(). + * @param aParentReflowInput A reference to an ReflowInput object that + * is to be the parent of this object. + * @param aFrame The frame for whose reflow input is being constructed. + * @param aAvailableSpace The available space to reflow aFrame (in aFrame's + * writing-mode). See comments for mAvailableSize for more information. + * @param aContainingBlockSize An optional size (in aFrame's writing mode), + * specifying the containing block size to use instead of the default + * size computed by ComputeContainingBlockRectangle(). If + * InitFlag::CallerWillInit is used, this is ignored. Pass it via + * Init() instead. + * @param aFlags A set of flags used for additional boolean parameters (see + * InitFlags above). + * @param aStyleSizeOverrides The style data used to override mFrame's when we + * call nsIFrame::ComputeSize() internally. + * @param aComputeSizeFlags A set of flags used when we call + * nsIFrame::ComputeSize() internally. + */ + ReflowInput(nsPresContext* aPresContext, + const ReflowInput& aParentReflowInput, nsIFrame* aFrame, + const mozilla::LogicalSize& aAvailableSpace, + const mozilla::Maybe& aContainingBlockSize = + mozilla::Nothing(), + InitFlags aFlags = {}, + const mozilla::StyleSizeOverrides& aSizeOverrides = {}, + mozilla::ComputeSizeFlags aComputeSizeFlags = {}); + + /** + * This method initializes various data members. It is automatically called by + * the constructors if InitFlags::CallerWillInit is *not* used. + * + * @param aContainingBlockSize An optional size (in mFrame's writing mode), + * specifying the containing block size to use instead of the default + * size computed by ComputeContainingBlockRectangle(). + * @param aBorder An optional border (in mFrame's writing mode). If given, use + * it instead of the border computed from mFrame's StyleBorder. + * @param aPadding An optional padding (in mFrame's writing mode). If given, + * use it instead of the padding computing from mFrame's StylePadding. + */ + void Init(nsPresContext* aPresContext, + const mozilla::Maybe& aContainingBlockSize = + mozilla::Nothing(), + const mozilla::Maybe& aBorder = + mozilla::Nothing(), + const mozilla::Maybe& aPadding = + mozilla::Nothing()); + + /** + * Get the used line-height property. The return value will be >= 0. + */ + nscoord GetLineHeight() const; + + /** + * Set the used line-height. aLineHeight must be >= 0. + */ + void SetLineHeight(nscoord aLineHeight); + + /** + * Calculate the used line-height property without a reflow input instance. + * The return value will be >= 0. + * + * @param aBlockBSize The computed block size of the content rect of the block + * that the line should fill. Only used with + * line-height:-moz-block-height. NS_UNCONSTRAINEDSIZE + * results in a normal line-height for + * line-height:-moz-block-height. + * @param aFontSizeInflation The result of the appropriate + * nsLayoutUtils::FontSizeInflationFor call, + * or 1.0 if during intrinsic size + * calculation. + */ + static nscoord CalcLineHeight(const ComputedStyle&, + nsPresContext* aPresContext, + const nsIContent* aContent, nscoord aBlockBSize, + float aFontSizeInflation); + + static nscoord CalcLineHeight(const StyleLineHeight&, + const nsStyleFont& aRelativeToFont, + nsPresContext* aPresContext, bool aIsVertical, + const nsIContent* aContent, nscoord aBlockBSize, + float aFontSizeInflation); + + mozilla::LogicalSize ComputeContainingBlockRectangle( + nsPresContext* aPresContext, const ReflowInput* aContainingBlockRI) const; + + /** + * Apply the mComputed(Min/Max)Width constraints to the content + * size computed so far. + */ + nscoord ApplyMinMaxWidth(nscoord aWidth) const { + if (NS_UNCONSTRAINEDSIZE != ComputedMaxWidth()) { + aWidth = std::min(aWidth, ComputedMaxWidth()); + } + return std::max(aWidth, ComputedMinWidth()); + } + + /** + * Apply the mComputed(Min/Max)ISize constraints to the content + * size computed so far. + */ + nscoord ApplyMinMaxISize(nscoord aISize) const { + if (NS_UNCONSTRAINEDSIZE != ComputedMaxISize()) { + aISize = std::min(aISize, ComputedMaxISize()); + } + return std::max(aISize, ComputedMinISize()); + } + + /** + * Apply the mComputed(Min/Max)Height constraints to the content + * size computed so far. + * + * @param aHeight The height that we've computed an to which we want to apply + * min/max constraints. + * @param aConsumed The amount of the computed height that was consumed by + * our prev-in-flows. + */ + nscoord ApplyMinMaxHeight(nscoord aHeight, nscoord aConsumed = 0) const { + aHeight += aConsumed; + + if (NS_UNCONSTRAINEDSIZE != ComputedMaxHeight()) { + aHeight = std::min(aHeight, ComputedMaxHeight()); + } + + if (NS_UNCONSTRAINEDSIZE != ComputedMinHeight()) { + aHeight = std::max(aHeight, ComputedMinHeight()); + } + + return aHeight - aConsumed; + } + + /** + * Apply the mComputed(Min/Max)BSize constraints to the content + * size computed so far. + * + * @param aBSize The block-size that we've computed an to which we want to + * apply min/max constraints. + * @param aConsumed The amount of the computed block-size that was consumed by + * our prev-in-flows. + */ + nscoord ApplyMinMaxBSize(nscoord aBSize, nscoord aConsumed = 0) const { + aBSize += aConsumed; + + if (NS_UNCONSTRAINEDSIZE != ComputedMaxBSize()) { + aBSize = std::min(aBSize, ComputedMaxBSize()); + } + + if (NS_UNCONSTRAINEDSIZE != ComputedMinBSize()) { + aBSize = std::max(aBSize, ComputedMinBSize()); + } + + return aBSize - aConsumed; + } + + bool ShouldReflowAllKids() const; + + // This method doesn't apply min/max computed widths to the value passed in. + void SetComputedWidth(nscoord aComputedWidth) { + if (mWritingMode.IsVertical()) { + SetComputedBSize(aComputedWidth); + } else { + SetComputedISize(aComputedWidth); + } + } + + // This method doesn't apply min/max computed heights to the value passed in. + void SetComputedHeight(nscoord aComputedHeight) { + if (mWritingMode.IsVertical()) { + SetComputedISize(aComputedHeight); + } else { + SetComputedBSize(aComputedHeight); + } + } + + // Use "No" to request SetComputedISize/SetComputedBSize not to reset resize + // flags. + enum class ResetResizeFlags : bool { No, Yes }; + + // This method doesn't apply min/max computed inline-sizes to the value passed + // in. + void SetComputedISize(nscoord aComputedISize, + ResetResizeFlags aFlags = ResetResizeFlags::Yes); + + // These methods don't apply min/max computed block-sizes to the value passed + // in. + void SetComputedBSize(nscoord aComputedBSize, + ResetResizeFlags aFlags = ResetResizeFlags::Yes); + + bool WillReflowAgainForClearance() const { + return mDiscoveredClearance && *mDiscoveredClearance; + } + + // Returns true if we should apply automatic minimum on the block axis. + // + // The automatic minimum size in the ratio-dependent axis of a box with a + // preferred aspect ratio that is neither a replaced element nor a scroll + // container is its min-content size clamped from above by its maximum size. + // + // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum + bool ShouldApplyAutomaticMinimumOnBlockAxis() const; + + // Returns true if mFrame has a constrained available block-size, or if mFrame + // is a continuation. When this method returns true, mFrame can be considered + // to be in a "fragmented context." + // + // Note: this method usually returns true when mFrame is in a paged + // environment (e.g. printing) or has a multi-column container ancestor. + // However, this doesn't include several cases when we're intentionally + // performing layout in a fragmentation-ignoring way, e.g. 1) mFrame is a flex + // or grid item, and this ReflowInput is for a measuring reflow with an + // unconstrained available block-size, or 2) mFrame is (or is inside of) an + // element that forms an orthogonal writing-mode. + bool IsInFragmentedContext() const; + + // Compute the offsets for a relative position element + // + // @param aWM the writing mode of aCBSize and the returned offsets. + static mozilla::LogicalMargin ComputeRelativeOffsets( + mozilla::WritingMode aWM, nsIFrame* aFrame, + const mozilla::LogicalSize& aCBSize); + + // If aFrame is a relatively or sticky positioned element, adjust aPosition + // appropriately. + // + // @param aComputedOffsets aFrame's relative offset, either from the cached + // nsIFrame::ComputedOffsetProperty() or ComputedPhysicalOffsets(). + // Note: This parameter is used only when aFrame is relatively + // positioned, not sticky positioned. + // @param aPosition [in/out] Pass aFrame's normal position (pre-relative + // positioning), and this method will update it to indicate aFrame's + // actual position. + static void ApplyRelativePositioning(nsIFrame* aFrame, + const nsMargin& aComputedOffsets, + nsPoint* aPosition); + + static void ApplyRelativePositioning( + nsIFrame* aFrame, mozilla::WritingMode aWritingMode, + const mozilla::LogicalMargin& aComputedOffsets, + mozilla::LogicalPoint* aPosition, const nsSize& aContainerSize); + + // Resolve any block-axis 'auto' margins (if any) for an absolutely positioned + // frame. aMargin and aOffsets are both outparams (though we only touch + // aOffsets if the position is overconstrained) + static void ComputeAbsPosBlockAutoMargin(nscoord aAvailMarginSpace, + WritingMode aContainingBlockWM, + bool aIsMarginBStartAuto, + bool aIsMarginBEndAuto, + LogicalMargin& aMargin, + LogicalMargin& aOffsets); + + // Resolve any inline-axis 'auto' margins (if any) for an absolutely + // positioned frame. aMargin and aOffsets are both outparams (though we only + // touch aOffsets if the position is overconstrained) + static void ComputeAbsPosInlineAutoMargin(nscoord aAvailMarginSpace, + WritingMode aContainingBlockWM, + bool aIsMarginIStartAuto, + bool aIsMarginIEndAuto, + LogicalMargin& aMargin, + LogicalMargin& aOffsets); + +#ifdef DEBUG + // Reflow trace methods. Defined in nsFrame.cpp so they have access + // to the display-reflow infrastructure. + static void* DisplayInitConstraintsEnter(nsIFrame* aFrame, + ReflowInput* aState, + nscoord aCBISize, nscoord aCBBSize, + const nsMargin* aBorder, + const nsMargin* aPadding); + static void DisplayInitConstraintsExit(nsIFrame* aFrame, ReflowInput* aState, + void* aValue); + static void* DisplayInitFrameTypeEnter(nsIFrame* aFrame, ReflowInput* aState); + static void DisplayInitFrameTypeExit(nsIFrame* aFrame, ReflowInput* aState, + void* aValue); +#endif + + protected: + void InitCBReflowInput(); + void InitResizeFlags(nsPresContext* aPresContext, + mozilla::LayoutFrameType aFrameType); + void InitDynamicReflowRoot(); + + void InitConstraints( + nsPresContext* aPresContext, + const mozilla::Maybe& aContainingBlockSize, + const mozilla::Maybe& aBorder, + const mozilla::Maybe& aPadding, + mozilla::LayoutFrameType aFrameType); + + // Returns the nearest containing block or block frame (whether or not + // it is a containing block) for the specified frame. Also returns + // the inline-start edge and logical size of the containing block's + // content area. + // These are returned in the coordinate space of the containing block. + nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame, + nscoord& aCBIStartEdge, + mozilla::LogicalSize& aCBSize) const; + + // Calculate a "hypothetical box" position where the placeholder frame + // (for a position:fixed/absolute element) would have been placed if it were + // positioned statically. The hypothetical box position will have a writing + // mode with the same block direction as the absolute containing block + // (aCBReflowInput->frame), though it may differ in inline direction. + void CalculateHypotheticalPosition(nsPresContext* aPresContext, + nsPlaceholderFrame* aPlaceholderFrame, + const ReflowInput* aCBReflowInput, + nsHypotheticalPosition& aHypotheticalPos, + mozilla::LayoutFrameType aFrameType) const; + + // Check if we can use the resolved auto block size (by insets) to compute + // the inline size through aspect-ratio on absolute-positioned elements. + // This is only needed for non-replaced elements. + // https://drafts.csswg.org/css-position/#abspos-auto-size + bool IsInlineSizeComputableByBlockSizeAndAspectRatio( + nscoord aBlockSize) const; + + // This calculates the size by using the resolved auto block size (from + // non-auto block insets), according to the writing mode of current block. + LogicalSize CalculateAbsoluteSizeWithResolvedAutoBlockSize( + nscoord aAutoBSize, const LogicalSize& aTentativeComputedSize); + + void InitAbsoluteConstraints(nsPresContext* aPresContext, + const ReflowInput* aCBReflowInput, + const mozilla::LogicalSize& aContainingBlockSize, + mozilla::LayoutFrameType aFrameType); + + // Calculates the computed values for the 'min-inline-size', + // 'max-inline-size', 'min-block-size', and 'max-block-size' properties, and + // stores them in the assorted data members + void ComputeMinMaxValues(const mozilla::LogicalSize& aCBSize); + + // aInsideBoxSizing returns the part of the padding, border, and margin + // in the aAxis dimension that goes inside the edge given by box-sizing; + // aOutsideBoxSizing returns the rest. + void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis, + nscoord aContainingBlockSize, + nscoord* aInsideBoxSizing, + nscoord* aOutsideBoxSizing) const; + + void CalculateBlockSideMargins(); + + /** + * @return true if mFrame is an internal table frame, i.e. an + * ns[RowGroup|ColGroup|Row|Cell]Frame. (We exclude nsTableColFrame + * here since we never setup a ReflowInput for those.) + */ + bool IsInternalTableFrame() const; + + private: + // The available size in which to reflow the frame. The space represents the + // amount of room for the frame's margin, border, padding, and content area. + // + // The available inline-size should be constrained. The frame's inline-size + // you choose should fit within it. + + // In galley mode, the available block-size is always unconstrained, and only + // page mode or multi-column layout involves a constrained available + // block-size. + // + // An unconstrained available block-size means you can choose whatever size + // you want. If the value is constrained, the frame's block-start border, + // padding, and content, must fit. If a frame is fully-complete after reflow, + // then its block-end border, padding, and margin (and similar for its + // fully-complete ancestors) will need to fit within this available + // block-size. However, if a frame is monolithic, it may choose a block-size + // larger than the available block-size. + mozilla::LogicalSize mAvailableSize{mWritingMode}; + + // The computed size specifies the frame's content area, and it does not apply + // to inline non-replaced elements. + // + // For block-level frames, the computed inline-size is based on the + // inline-size of the containing block, the margin/border/padding areas, and + // the min/max inline-size. + // + // For non-replaced block-level frames in the flow and floated, if the + // computed block-size is NS_UNCONSTRAINEDSIZE, you should choose a block-size + // to shrink wrap around the normal flow child frames. The block-size must be + // within the limit of the min/max block-size if there is such a limit. + mozilla::LogicalSize mComputedSize{mWritingMode}; + + // Computed values for 'inset' properties. Only applies to 'positioned' + // elements. + mozilla::LogicalMargin mComputedOffsets{mWritingMode}; + + // Computed value for 'min-inline-size'/'min-block-size'. + mozilla::LogicalSize mComputedMinSize{mWritingMode}; + + // Computed value for 'max-inline-size'/'max-block-size'. + mozilla::LogicalSize mComputedMaxSize{mWritingMode, NS_UNCONSTRAINEDSIZE, + NS_UNCONSTRAINEDSIZE}; + + // Cache the used line-height property. + mutable nscoord mLineHeight = NS_UNCONSTRAINEDSIZE; +}; + +} // namespace mozilla + +#endif // mozilla_ReflowInput_h diff --git a/layout/generic/ReflowOutput.cpp b/layout/generic/ReflowOutput.cpp new file mode 100644 index 0000000000..a0312dffda --- /dev/null +++ b/layout/generic/ReflowOutput.cpp @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* struct containing the output from nsIFrame::Reflow */ + +#include "mozilla/ReflowOutput.h" +#include "mozilla/ReflowInput.h" + +namespace mozilla { + +static bool IsValidOverflowRect(const nsRect& aRect) { + // `IsEmpty` in the context of `nsRect` means "width OR height is zero." + // However, in the context of overflow, the rect having one axis as zero is + // NOT considered empty. + if (MOZ_LIKELY(!aRect.IsEmpty())) { + return true; + } + + // Be defensive and consider rects with any negative size as invalid. + return !aRect.IsEqualEdges(nsRect()) && aRect.Width() >= 0 && + aRect.Height() >= 0; +} + +/* static */ +nsRect OverflowAreas::GetOverflowClipRect(const nsRect& aRectToClip, + const nsRect& aBounds, + PhysicalAxes aClipAxes, + const nsSize& aOverflowMargin) { + auto inflatedBounds = aBounds; + inflatedBounds.Inflate(aOverflowMargin); + auto clip = aRectToClip; + if (aClipAxes & PhysicalAxes::Vertical) { + clip.y = inflatedBounds.y; + clip.height = inflatedBounds.height; + } + if (aClipAxes & PhysicalAxes::Horizontal) { + clip.x = inflatedBounds.x; + clip.width = inflatedBounds.width; + } + return clip; +} + +/* static */ +void OverflowAreas::ApplyOverflowClippingOnRect(nsRect& aOverflowRect, + const nsRect& aBounds, + PhysicalAxes aClipAxes, + const nsSize& aOverflowMargin) { + aOverflowRect = aOverflowRect.Intersect( + GetOverflowClipRect(aOverflowRect, aBounds, aClipAxes, aOverflowMargin)); +} + +void OverflowAreas::UnionWith(const OverflowAreas& aOther) { + if (IsValidOverflowRect(aOther.InkOverflow())) { + InkOverflow().UnionRect(InkOverflow(), aOther.InkOverflow()); + } + if (IsValidOverflowRect(aOther.ScrollableOverflow())) { + ScrollableOverflow().UnionRect(ScrollableOverflow(), + aOther.ScrollableOverflow()); + } +} + +void OverflowAreas::UnionAllWith(const nsRect& aRect) { + if (!IsValidOverflowRect(aRect)) { + // Same as `UnionWith()` - avoid losing information. + return; + } + InkOverflow().UnionRect(InkOverflow(), aRect); + ScrollableOverflow().UnionRect(ScrollableOverflow(), aRect); +} + +void OverflowAreas::SetAllTo(const nsRect& aRect) { + InkOverflow() = aRect; + ScrollableOverflow() = aRect; +} + +ReflowOutput::ReflowOutput(const ReflowInput& aReflowInput) + : ReflowOutput(aReflowInput.GetWritingMode()) {} + +void ReflowOutput::SetOverflowAreasToDesiredBounds() { + mOverflowAreas.SetAllTo(nsRect(0, 0, Width(), Height())); +} + +void ReflowOutput::UnionOverflowAreasWithDesiredBounds() { + mOverflowAreas.UnionAllWith(nsRect(0, 0, Width(), Height())); +} + +} // namespace mozilla diff --git a/layout/generic/ReflowOutput.h b/layout/generic/ReflowOutput.h new file mode 100644 index 0000000000..ab71fa75a4 --- /dev/null +++ b/layout/generic/ReflowOutput.h @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* struct containing the output from nsIFrame::Reflow */ + +#ifndef mozilla_ReflowOutput_h +#define mozilla_ReflowOutput_h + +#include "mozilla/EnumeratedRange.h" +#include "mozilla/WritingModes.h" +#include "nsBoundingMetrics.h" +#include "nsRect.h" + +//---------------------------------------------------------------------- + +namespace mozilla { +struct ReflowInput; + +enum class OverflowType : uint8_t { Ink, Scrollable }; +constexpr auto AllOverflowTypes() { + return mozilla::MakeInclusiveEnumeratedRange(OverflowType::Ink, + OverflowType::Scrollable); +} + +struct OverflowAreas { + public: + nsRect& InkOverflow() { return mInk; } + const nsRect& InkOverflow() const { return mInk; } + + nsRect& ScrollableOverflow() { return mScrollable; } + const nsRect& ScrollableOverflow() const { return mScrollable; } + + nsRect& Overflow(OverflowType aType) { + return aType == OverflowType::Ink ? InkOverflow() : ScrollableOverflow(); + } + const nsRect& Overflow(OverflowType aType) const { + return aType == OverflowType::Ink ? InkOverflow() : ScrollableOverflow(); + } + + OverflowAreas() = default; + + OverflowAreas(const nsRect& aInkOverflow, const nsRect& aScrollableOverflow) + : mInk(aInkOverflow), mScrollable(aScrollableOverflow) {} + + bool operator==(const OverflowAreas& aOther) const { + // Scrollable overflow is a point-set rectangle and ink overflow + // is a pixel-set rectangle. + return InkOverflow().IsEqualInterior(aOther.InkOverflow()) && + ScrollableOverflow().IsEqualEdges(aOther.ScrollableOverflow()); + } + + bool operator!=(const OverflowAreas& aOther) const { + return !(*this == aOther); + } + + OverflowAreas operator+(const nsPoint& aPoint) const { + OverflowAreas result(*this); + result += aPoint; + return result; + } + + OverflowAreas& operator+=(const nsPoint& aPoint) { + mInk += aPoint; + mScrollable += aPoint; + return *this; + } + + void Clear() { SetAllTo(nsRect()); } + + // Mutates |this| by unioning both overflow areas with |aOther|. + void UnionWith(const OverflowAreas& aOther); + + // Mutates |this| by unioning both overflow areas with |aRect|. + void UnionAllWith(const nsRect& aRect); + + // Mutates |this| by setting both overflow areas to |aRect|. + void SetAllTo(const nsRect& aRect); + + // Applies overflow clipping (for e.g. overflow: clip) as needed to both our + // overflow rects. + void ApplyClipping(const nsRect& aBounds, PhysicalAxes aClipAxes, + const nsSize& aOverflowMargin) { + ApplyOverflowClippingOnRect(InkOverflow(), aBounds, aClipAxes, + aOverflowMargin); + ApplyOverflowClippingOnRect(ScrollableOverflow(), aBounds, aClipAxes, + aOverflowMargin); + } + + // Gets the overflow clipping rect for a given element given a rect to clip, + // the frame bounds, a set of axes, and the overflow margin. + static nsRect GetOverflowClipRect(const nsRect& aRectToClip, + const nsRect& aBounds, + PhysicalAxes aClipAxes, + const nsSize& aOverflowMargin); + + // Applies the overflow clipping to a given overflow rect, given the frame + // bounds, and the physical axes on which to apply the overflow clip. + static void ApplyOverflowClippingOnRect(nsRect& aOverflowRect, + const nsRect& aBounds, + PhysicalAxes aClipAxes, + const nsSize& aOverflowMargin); + + private: + nsRect mInk; + nsRect mScrollable; +}; + +} // namespace mozilla + +/** + * An nsCollapsingMargin represents a vertical collapsing margin between + * blocks as described in section 8.3.1 of CSS2, + * . + * + * All adjacent vertical margins collapse, and the resulting margin is + * the sum of the largest positive margin included and the smallest (most + * negative) negative margin included. + */ +struct nsCollapsingMargin { + private: + nscoord mMostPos; // the largest positive margin included + nscoord mMostNeg; // the smallest negative margin included + + public: + nsCollapsingMargin() : mMostPos(0), mMostNeg(0) {} + + nsCollapsingMargin(const nsCollapsingMargin& aOther) = default; + + bool operator==(const nsCollapsingMargin& aOther) const { + return mMostPos == aOther.mMostPos && mMostNeg == aOther.mMostNeg; + } + + bool operator!=(const nsCollapsingMargin& aOther) const { + return !(*this == aOther); + } + + nsCollapsingMargin& operator=(const nsCollapsingMargin& aOther) = default; + + void Include(nscoord aCoord) { + if (aCoord > mMostPos) + mMostPos = aCoord; + else if (aCoord < mMostNeg) + mMostNeg = aCoord; + } + + void Include(const nsCollapsingMargin& aOther) { + if (aOther.mMostPos > mMostPos) mMostPos = aOther.mMostPos; + if (aOther.mMostNeg < mMostNeg) mMostNeg = aOther.mMostNeg; + } + + void Zero() { + mMostPos = 0; + mMostNeg = 0; + } + + bool IsZero() const { return (mMostPos == 0) && (mMostNeg == 0); } + + nscoord get() const { return mMostPos + mMostNeg; } +}; + +namespace mozilla { + +/** + * ReflowOutput is initialized by a parent frame as a parameter passing to + * Reflow() to allow a child frame to return its desired size and alignment + * information. + * + * ReflowOutput's constructor usually takes a parent frame's WritingMode (or + * ReflowInput) because it is more convenient for the parent frame to use the + * stored Size() after reflowing the child frame. However, it can actually + * accept any WritingMode (or ReflowInput) because SetSize() knows how to + * convert a size in any writing mode to the stored writing mode. + * + * @see nsIFrame::Reflow() for more information. + */ +class ReflowOutput { + public: + explicit ReflowOutput(mozilla::WritingMode aWritingMode) + : mSize(aWritingMode), mWritingMode(aWritingMode) {} + + // A convenient constructor to get WritingMode in ReflowInput. + explicit ReflowOutput(const ReflowInput& aReflowInput); + + nscoord ISize(mozilla::WritingMode aWritingMode) const { + return mSize.ISize(aWritingMode); + } + nscoord BSize(mozilla::WritingMode aWritingMode) const { + return mSize.BSize(aWritingMode); + } + mozilla::LogicalSize Size(mozilla::WritingMode aWritingMode) const { + return mSize.ConvertTo(aWritingMode, mWritingMode); + } + + nscoord& ISize(mozilla::WritingMode aWritingMode) { + return mSize.ISize(aWritingMode); + } + nscoord& BSize(mozilla::WritingMode aWritingMode) { + return mSize.BSize(aWritingMode); + } + + // Set inline and block size from a LogicalSize, converting to our + // writing mode as necessary. + void SetSize(mozilla::WritingMode aWM, mozilla::LogicalSize aSize) { + mSize = aSize.ConvertTo(mWritingMode, aWM); + } + + // Set both inline and block size to zero -- no need for a writing mode! + void ClearSize() { mSize.SizeTo(mWritingMode, 0, 0); } + + // Width and Height are physical dimensions, independent of writing mode. + // Accessing these is slightly more expensive than accessing the logical + // dimensions; as far as possible, client code should work purely with logical + // dimensions. + nscoord Width() const { return mSize.Width(mWritingMode); } + nscoord Height() const { return mSize.Height(mWritingMode); } + nscoord& Width() { + return mWritingMode.IsVertical() ? mSize.BSize(mWritingMode) + : mSize.ISize(mWritingMode); + } + nscoord& Height() { + return mWritingMode.IsVertical() ? mSize.ISize(mWritingMode) + : mSize.BSize(mWritingMode); + } + + nsSize PhysicalSize() const { return mSize.GetPhysicalSize(mWritingMode); } + + // It's only meaningful to consider "ascent" on the block-start side of the + // frame, so no need to pass a writing mode argument + enum { ASK_FOR_BASELINE = nscoord_MAX }; + nscoord BlockStartAscent() const { return mBlockStartAscent; } + void SetBlockStartAscent(nscoord aAscent) { mBlockStartAscent = aAscent; } + + // Metrics that _exactly_ enclose the text to allow precise MathML placements. + nsBoundingMetrics mBoundingMetrics; // [OUT] + + // Carried out block-end margin values. This is the collapsed + // (generational) block-end margin value. + nsCollapsingMargin mCarriedOutBEndMargin; + + // For frames that have content that overflow their content area + // (HasOverflowAreas() is true) these rectangles represent the total + // area of the frame including visible overflow, i.e., don't include + // overflowing content that is hidden. The rects are in the local + // coordinate space of the frame, and should be at least as big as the + // desired size. If there is no content that overflows, then the + // overflow area is identical to the desired size and should be {0, 0, + // width, height}. + OverflowAreas mOverflowAreas; + + nsRect& InkOverflow() { return mOverflowAreas.InkOverflow(); } + const nsRect& InkOverflow() const { return mOverflowAreas.InkOverflow(); } + nsRect& ScrollableOverflow() { return mOverflowAreas.ScrollableOverflow(); } + const nsRect& ScrollableOverflow() const { + return mOverflowAreas.ScrollableOverflow(); + } + + // Set all of mOverflowAreas to (0, 0, width, height). + void SetOverflowAreasToDesiredBounds(); + + // Union all of mOverflowAreas with (0, 0, width, height). + void UnionOverflowAreasWithDesiredBounds(); + + mozilla::WritingMode GetWritingMode() const { return mWritingMode; } + + private: + // Desired size of a frame's border-box. + LogicalSize mSize; + + // Baseline (in block direction), or the default value ASK_FOR_BASELINE. + nscoord mBlockStartAscent = ASK_FOR_BASELINE; + + mozilla::WritingMode mWritingMode; +}; + +} // namespace mozilla + +#endif // mozilla_ReflowOutput_h diff --git a/layout/generic/RubyUtils.cpp b/layout/generic/RubyUtils.cpp new file mode 100644 index 0000000000..4cdd4b65b3 --- /dev/null +++ b/layout/generic/RubyUtils.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RubyUtils.h" +#include "nsRubyFrame.h" +#include "nsRubyBaseFrame.h" +#include "nsRubyTextFrame.h" +#include "nsRubyBaseContainerFrame.h" +#include "nsRubyTextContainerFrame.h" + +using namespace mozilla; + +NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord) + +/* static */ +void RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize) { + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + aFrame->SetProperty(ReservedISize(), aISize); +} + +/* static */ +void RubyUtils::ClearReservedISize(nsIFrame* aFrame) { + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + aFrame->RemoveProperty(ReservedISize()); +} + +/* static */ +nscoord RubyUtils::GetReservedISize(nsIFrame* aFrame) { + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + return aFrame->GetProperty(ReservedISize()); +} + +AutoRubyTextContainerArray::AutoRubyTextContainerArray( + nsRubyBaseContainerFrame* aBaseContainer) { + for (nsIFrame* frame = aBaseContainer->GetNextSibling(); + frame && frame->IsRubyTextContainerFrame(); + frame = frame->GetNextSibling()) { + AppendElement(static_cast(frame)); + } +} + +nsIFrame* RubyColumn::Iterator::operator*() const { + nsIFrame* frame; + if (mIndex == -1) { + frame = mColumn.mBaseFrame; + } else { + frame = mColumn.mTextFrames[mIndex]; + } + MOZ_ASSERT(frame, "Frame here cannot be null"); + return frame; +} + +void RubyColumn::Iterator::SkipUntilExistingFrame() { + if (mIndex == -1) { + if (mColumn.mBaseFrame) { + return; + } + ++mIndex; + } + int32_t numTextFrames = mColumn.mTextFrames.Length(); + for (; mIndex < numTextFrames; ++mIndex) { + if (mColumn.mTextFrames[mIndex]) { + break; + } + } +} + +RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame) { + nsIFrame* frame = aRubyFrame->PrincipalChildList().FirstChild(); + MOZ_ASSERT(!frame || frame->IsRubyBaseContainerFrame()); + mBaseContainer = static_cast(frame); +} + +void RubySegmentEnumerator::Next() { + MOZ_ASSERT(mBaseContainer); + nsIFrame* frame = mBaseContainer->GetNextSibling(); + while (frame && !frame->IsRubyBaseContainerFrame()) { + frame = frame->GetNextSibling(); + } + mBaseContainer = static_cast(frame); +} + +RubyColumnEnumerator::RubyColumnEnumerator( + nsRubyBaseContainerFrame* aBaseContainer, + const AutoRubyTextContainerArray& aTextContainers) + : mAtIntraLevelWhitespace(false) { + const uint32_t rtcCount = aTextContainers.Length(); + mFrames.SetCapacity(rtcCount + 1); + + nsIFrame* rbFrame = aBaseContainer->PrincipalChildList().FirstChild(); + MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame()); + mFrames.AppendElement(static_cast(rbFrame)); + for (uint32_t i = 0; i < rtcCount; i++) { + nsRubyTextContainerFrame* container = aTextContainers[i]; + // If the container is for span, leave a nullptr here. + // Spans do not take part in pairing. + nsIFrame* rtFrame = !container->IsSpanContainer() + ? container->PrincipalChildList().FirstChild() + : nullptr; + MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame()); + mFrames.AppendElement(static_cast(rtFrame)); + } + + // We have to init mAtIntraLevelWhitespace to be correct for the + // first column. There are two ways we could end up with intra-level + // whitespace in our first colum: + // 1. The current segment itself is an inter-segment whitespace; + // 2. If our ruby segment is split across multiple lines, and some + // intra-level whitespace happens to fall right after a line-break. + // Each line will get its own nsRubyBaseContainerFrame, and the + // container right after the line-break will end up with its first + // column containing that intra-level whitespace. + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* frame = mFrames[i]; + if (frame && frame->IsIntraLevelWhitespace()) { + mAtIntraLevelWhitespace = true; + break; + } + } +} + +void RubyColumnEnumerator::Next() { + bool advancingToIntraLevelWhitespace = false; + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* frame = mFrames[i]; + // If we've got intra-level whitespace frames at some levels in the + // current ruby column, we "faked" an anonymous box for all other + // levels for this column. So when we advance off this column, we + // don't advance any of the frames in those levels, because we're + // just advancing across the "fake" frames. + if (frame && + (!mAtIntraLevelWhitespace || frame->IsIntraLevelWhitespace())) { + nsIFrame* nextSibling = frame->GetNextSibling(); + MOZ_ASSERT(!nextSibling || nextSibling->Type() == frame->Type(), + "Frame type should be identical among a level"); + mFrames[i] = frame = static_cast(nextSibling); + if (!advancingToIntraLevelWhitespace && frame && + frame->IsIntraLevelWhitespace()) { + advancingToIntraLevelWhitespace = true; + } + } + } + MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace, + "Should never have adjacent intra-level whitespace columns"); + mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace; +} + +bool RubyColumnEnumerator::AtEnd() const { + for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { + if (mFrames[i]) { + return false; + } + } + return true; +} + +nsRubyContentFrame* RubyColumnEnumerator::GetFrameAtLevel( + uint32_t aIndex) const { + // If the current ruby column is for intra-level whitespaces, we + // return nullptr for any levels that do not have an actual intra- + // level whitespace frame in this column. This nullptr represents + // an anonymous empty intra-level whitespace box. (In this case, + // it's important that we NOT return mFrames[aIndex], because it's + // really part of the next column, not the current one.) + nsRubyContentFrame* frame = mFrames[aIndex]; + return !mAtIntraLevelWhitespace || (frame && frame->IsIntraLevelWhitespace()) + ? frame + : nullptr; +} + +void RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const { + nsRubyContentFrame* rbFrame = GetFrameAtLevel(0); + MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame()); + aColumn.mBaseFrame = static_cast(rbFrame); + aColumn.mTextFrames.ClearAndRetainStorage(); + for (uint32_t i = 1, iend = mFrames.Length(); i < iend; i++) { + nsRubyContentFrame* rtFrame = GetFrameAtLevel(i); + MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame()); + aColumn.mTextFrames.AppendElement(static_cast(rtFrame)); + } + aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace; +} diff --git a/layout/generic/RubyUtils.h b/layout/generic/RubyUtils.h new file mode 100644 index 0000000000..cd2019d100 --- /dev/null +++ b/layout/generic/RubyUtils.h @@ -0,0 +1,223 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_RubyUtils_h_ +#define mozilla_RubyUtils_h_ + +#include "nsCSSAnonBoxes.h" +#include "nsGkAtoms.h" +#include "nsIFrame.h" +#include "nsTArray.h" + +#define RTC_ARRAY_SIZE 1 + +class nsRubyFrame; +class nsRubyBaseFrame; +class nsRubyTextFrame; +class nsRubyContentFrame; +class nsRubyBaseContainerFrame; +class nsRubyTextContainerFrame; + +namespace mozilla { + +/** + * Reserved ISize + * + * With some exceptions, each ruby internal box has two isizes, which + * are the reflowed isize and the final isize. The reflowed isize is + * what a box itself needs. It is determined when the box gets reflowed. + * + * The final isize is what a box should be as the final result. For a + * ruby base/text box, the final isize is the size of its ruby column. + * For a ruby base/text container, the final isize is the size of its + * ruby segment. The final isize is never smaller than the reflowed + * isize. It is initially determined when a ruby column/segment gets + * fully reflowed, and may be advanced when a box is expanded, e.g. + * for justification. + * + * The difference between the reflowed isize and the final isize is + * reserved in the line layout after reflowing a box, hence it is called + * "Reserved ISize" here. It is used to expand the ruby boxes from their + * reflowed isize to the final isize during alignment of the line. + * + * There are three exceptions for the final isize: + * 1. A ruby text container has a larger final isize only if it is for + * a span or collapsed annotations. + * 2. A ruby base container has a larger final isize only if at least + * one of its ruby text containers does. + * 3. If a ruby text container has a larger final isize, its children + * must not have. + */ + +class RubyUtils { + public: + static inline bool IsRubyContentBox(LayoutFrameType aFrameType) { + return aFrameType == mozilla::LayoutFrameType::RubyBase || + aFrameType == mozilla::LayoutFrameType::RubyText; + } + + static inline bool IsRubyContainerBox(LayoutFrameType aFrameType) { + return aFrameType == mozilla::LayoutFrameType::RubyBaseContainer || + aFrameType == mozilla::LayoutFrameType::RubyTextContainer; + } + + static inline bool IsRubyBox(LayoutFrameType aFrameType) { + return aFrameType == mozilla::LayoutFrameType::Ruby || + IsRubyContentBox(aFrameType) || IsRubyContainerBox(aFrameType); + } + + static inline bool IsExpandableRubyBox(nsIFrame* aFrame) { + mozilla::LayoutFrameType type = aFrame->Type(); + return IsRubyContentBox(type) || IsRubyContainerBox(type); + } + + static inline bool IsRubyPseudo(PseudoStyleType aPseudo) { + return aPseudo == PseudoStyleType::blockRubyContent || + aPseudo == PseudoStyleType::ruby || + aPseudo == PseudoStyleType::rubyBase || + aPseudo == PseudoStyleType::rubyText || + aPseudo == PseudoStyleType::rubyBaseContainer || + aPseudo == PseudoStyleType::rubyTextContainer; + } + + static void SetReservedISize(nsIFrame* aFrame, nscoord aISize); + static void ClearReservedISize(nsIFrame* aFrame); + static nscoord GetReservedISize(nsIFrame* aFrame); +}; + +/** + * This array stores all ruby text containers of the ruby segment + * of the given ruby base container. + */ +class MOZ_RAII AutoRubyTextContainerArray final + : public AutoTArray { + public: + explicit AutoRubyTextContainerArray(nsRubyBaseContainerFrame* aBaseContainer); +}; + +/** + * This enumerator enumerates each ruby segment. + */ +class MOZ_STACK_CLASS RubySegmentEnumerator { + public: + explicit RubySegmentEnumerator(nsRubyFrame* aRubyFrame); + + void Next(); + bool AtEnd() const { return !mBaseContainer; } + + nsRubyBaseContainerFrame* GetBaseContainer() const { return mBaseContainer; } + + private: + nsRubyBaseContainerFrame* mBaseContainer; +}; + +/** + * Ruby column is a unit consists of one ruby base and all ruby + * annotations paired with it. + * See http://dev.w3.org/csswg/css-ruby/#ruby-pairing + */ +struct MOZ_STACK_CLASS RubyColumn { + nsRubyBaseFrame* mBaseFrame; + AutoTArray mTextFrames; + bool mIsIntraLevelWhitespace; + + RubyColumn() : mBaseFrame(nullptr), mIsIntraLevelWhitespace(false) {} + + // Helper class to support iteration across the frames within a single + // RubyColumn (the column's ruby base and its annotations). + class MOZ_STACK_CLASS Iterator { + public: + nsIFrame* operator*() const; + + Iterator& operator++() { + ++mIndex; + SkipUntilExistingFrame(); + return *this; + } + Iterator operator++(int) { + auto ret = *this; + ++*this; + return ret; + } + + friend bool operator==(const Iterator& aIter1, const Iterator& aIter2) { + MOZ_ASSERT(&aIter1.mColumn == &aIter2.mColumn, + "Should only compare iterators of the same ruby column"); + return aIter1.mIndex == aIter2.mIndex; + } + friend bool operator!=(const Iterator& aIter1, const Iterator& aIter2) { + return !(aIter1 == aIter2); + } + + private: + Iterator(const RubyColumn& aColumn, int32_t aIndex) + : mColumn(aColumn), mIndex(aIndex) { + MOZ_ASSERT( + aIndex == -1 || + (aIndex >= 0 && aIndex <= int32_t(aColumn.mTextFrames.Length()))); + SkipUntilExistingFrame(); + } + friend struct RubyColumn; // for the constructor + + void SkipUntilExistingFrame(); + + const RubyColumn& mColumn; + // -1 means the ruby base frame, + // non-negative means the index of ruby text frame + // a value of mTextFrames.Length() means we're done iterating + int32_t mIndex = -1; + }; + + Iterator begin() const { return Iterator(*this, -1); } + Iterator end() const { return Iterator(*this, mTextFrames.Length()); } + Iterator cbegin() const { return begin(); } + Iterator cend() const { return end(); } +}; + +/** + * This enumerator enumerates ruby columns in a segment. + */ +class MOZ_STACK_CLASS RubyColumnEnumerator { + public: + RubyColumnEnumerator(nsRubyBaseContainerFrame* aRBCFrame, + const AutoRubyTextContainerArray& aRTCFrames); + + void Next(); + bool AtEnd() const; + + uint32_t GetLevelCount() const { return mFrames.Length(); } + nsRubyContentFrame* GetFrameAtLevel(uint32_t aIndex) const; + void GetColumn(RubyColumn& aColumn) const; + + private: + // Frames in this array are NOT necessary part of the current column. + // When in doubt, use GetFrameAtLevel to access it. + // See GetFrameAtLevel() and Next() for more info. + AutoTArray mFrames; + // Whether we are on a column for intra-level whitespaces + bool mAtIntraLevelWhitespace; +}; + +/** + * Stores block-axis leadings produced from ruby annotations. + */ +struct RubyBlockLeadings { + nscoord mStart = 0; + nscoord mEnd = 0; + + void Reset() { mStart = mEnd = 0; } + void Update(nscoord aStart, nscoord aEnd) { + mStart = std::max(mStart, aStart); + mEnd = std::max(mEnd, aEnd); + } + void Update(const RubyBlockLeadings& aOther) { + Update(aOther.mStart, aOther.mEnd); + } +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_RubyUtils_h_) */ diff --git a/layout/generic/ScrollAnchorContainer.cpp b/layout/generic/ScrollAnchorContainer.cpp new file mode 100644 index 0000000000..9e208a32b0 --- /dev/null +++ b/layout/generic/ScrollAnchorContainer.cpp @@ -0,0 +1,796 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScrollAnchorContainer.h" +#include + +#include "mozilla/dom/Text.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/ToString.h" +#include "nsBlockFrame.h" +#include "nsGfxScrollFrame.h" +#include "nsIFrame.h" +#include "nsIFrameInlines.h" +#include "nsLayoutUtils.h" +#include "nsPlaceholderFrame.h" + +using namespace mozilla::dom; + +#ifdef DEBUG +static mozilla::LazyLogModule sAnchorLog("scrollanchor"); + +# define ANCHOR_LOG_WITH(anchor_, fmt, ...) \ + MOZ_LOG(sAnchorLog, LogLevel::Debug, \ + ("ANCHOR(%p, %s, root: %d): " fmt, (anchor_), \ + (anchor_) \ + ->Frame() \ + ->PresContext() \ + ->Document() \ + ->GetDocumentURI() \ + ->GetSpecOrDefault() \ + .get(), \ + (anchor_)->Frame()->mIsRoot, ##__VA_ARGS__)); + +# define ANCHOR_LOG(fmt, ...) ANCHOR_LOG_WITH(this, fmt, ##__VA_ARGS__) +#else +# define ANCHOR_LOG(...) +# define ANCHOR_LOG_WITH(...) +#endif + +namespace mozilla::layout { + +nsHTMLScrollFrame* ScrollAnchorContainer::Frame() const { + return reinterpret_cast( + ((char*)this) - offsetof(nsHTMLScrollFrame, mAnchor)); +} + +ScrollAnchorContainer::ScrollAnchorContainer(nsHTMLScrollFrame* aScrollFrame) + : mDisabled(false), + mAnchorMightBeSubOptimal(false), + mAnchorNodeIsDirty(true), + mApplyingAnchorAdjustment(false), + mSuppressAnchorAdjustment(false) { + MOZ_ASSERT(aScrollFrame == Frame()); +} + +ScrollAnchorContainer::~ScrollAnchorContainer() = default; + +ScrollAnchorContainer* ScrollAnchorContainer::FindFor(nsIFrame* aFrame) { + aFrame = aFrame->GetParent(); + if (!aFrame) { + return nullptr; + } + nsIScrollableFrame* nearest = nsLayoutUtils::GetNearestScrollableFrame( + aFrame, nsLayoutUtils::SCROLLABLE_SAME_DOC | + nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); + if (nearest) { + return nearest->Anchor(); + } + return nullptr; +} + +nsIScrollableFrame* ScrollAnchorContainer::ScrollableFrame() const { + return Frame()->GetScrollTargetFrame(); +} + +/** + * Set the appropriate frame flags for a frame that has become or is no longer + * an anchor node. + */ +static void SetAnchorFlags(const nsIFrame* aScrolledFrame, + nsIFrame* aAnchorNode, bool aInScrollAnchorChain) { + nsIFrame* frame = aAnchorNode; + while (frame && frame != aScrolledFrame) { + // TODO(emilio, bug 1629280): This commented out assertion below should + // hold, but it may not in the case of reparenting-during-reflow (due to + // inline fragmentation or such). That looks fishy! + // + // We should either invalidate the anchor when reparenting any frame on the + // chain, or fix up the chain flags. + // + // MOZ_DIAGNOSTIC_ASSERT(frame->IsInScrollAnchorChain() != + // aInScrollAnchorChain); + frame->SetInScrollAnchorChain(aInScrollAnchorChain); + frame = frame->GetParent(); + } + MOZ_ASSERT(frame, + "The anchor node should be a descendant of the scrolled frame"); + // If needed, invalidate the frame so that we start/stop highlighting the + // anchor + if (StaticPrefs::layout_css_scroll_anchoring_highlight()) { + for (nsIFrame* frame = aAnchorNode->FirstContinuation(); !!frame; + frame = frame->GetNextContinuation()) { + frame->InvalidateFrame(); + } + } +} + +/** + * Compute the scrollable overflow rect [1] of aCandidate relative to + * aScrollFrame with all transforms applied. + * + * The specification is ambiguous about what can be selected as a scroll anchor, + * which makes the scroll anchoring bounding rect partially undefined [2]. This + * code attempts to match the implementation in Blink. + * + * An additional unspecified behavior is that any scrollable overflow before the + * border start edge in the block axis of aScrollFrame should be clamped. This + * is to prevent absolutely positioned descendant elements from being able to + * trigger scroll adjustments [3]. + * + * [1] + * https://drafts.csswg.org/css-scroll-anchoring-1/#scroll-anchoring-bounding-rect + * [2] https://github.com/w3c/csswg-drafts/issues/3478 + * [3] https://bugzilla.mozilla.org/show_bug.cgi?id=1519541 + */ +static nsRect FindScrollAnchoringBoundingRect(const nsIFrame* aScrollFrame, + nsIFrame* aCandidate) { + MOZ_ASSERT(nsLayoutUtils::IsProperAncestorFrame(aScrollFrame, aCandidate)); + if (!!Text::FromNodeOrNull(aCandidate->GetContent())) { + // This is a frame for a text node. The spec says we need to accumulate the + // union of all line boxes in the coordinate space of the scroll frame + // accounting for transforms. + // + // To do this, we translate and accumulate the overflow rect for each text + // continuation to the coordinate space of the nearest ancestor block + // frame. Then we transform the resulting rect into the coordinate space of + // the scroll frame. + // + // Transforms aren't allowed on non-replaced inline boxes, so we can assume + // that these text node continuations will have the same transform as their + // nearest block ancestor. And it should be faster to transform their union + // rather than individually transforming each overflow rect + // + // XXX for fragmented blocks, blockAncestor will be an ancestor only to the + // text continuations in the first block continuation. GetOffsetTo + // should continue to work, but is it correct with transforms or a + // performance hazard? + nsIFrame* blockAncestor = + nsLayoutUtils::FindNearestBlockAncestor(aCandidate); + MOZ_ASSERT( + nsLayoutUtils::IsProperAncestorFrame(aScrollFrame, blockAncestor)); + nsRect bounding; + for (nsIFrame* continuation = aCandidate->FirstContinuation(); continuation; + continuation = continuation->GetNextContinuation()) { + nsRect overflowRect = + continuation->ScrollableOverflowRectRelativeToSelf(); + overflowRect += continuation->GetOffsetTo(blockAncestor); + bounding = bounding.Union(overflowRect); + } + return nsLayoutUtils::TransformFrameRectToAncestor(blockAncestor, bounding, + aScrollFrame); + } + + nsRect borderRect = aCandidate->GetRectRelativeToSelf(); + nsRect overflowRect = aCandidate->ScrollableOverflowRectRelativeToSelf(); + + NS_ASSERTION(overflowRect.Contains(borderRect), + "overflow rect must include border rect, and the clamping logic " + "here depends on that"); + + // Clamp the scrollable overflow rect to the border start edge on the block + // axis of the scroll frame + WritingMode writingMode = aScrollFrame->GetWritingMode(); + switch (writingMode.GetBlockDir()) { + case WritingMode::eBlockTB: { + overflowRect.SetBoxY(borderRect.Y(), overflowRect.YMost()); + break; + } + case WritingMode::eBlockLR: { + overflowRect.SetBoxX(borderRect.X(), overflowRect.XMost()); + break; + } + case WritingMode::eBlockRL: { + overflowRect.SetBoxX(overflowRect.X(), borderRect.XMost()); + break; + } + } + + nsRect transformed = nsLayoutUtils::TransformFrameRectToAncestor( + aCandidate, overflowRect, aScrollFrame); + return transformed; +} + +/** + * Compute the offset between the scrollable overflow rect start edge of + * aCandidate and the scroll-port start edge of aScrollFrame, in the block axis + * of aScrollFrame. + */ +static nscoord FindScrollAnchoringBoundingOffset( + const nsHTMLScrollFrame* aScrollFrame, nsIFrame* aCandidate) { + WritingMode writingMode = aScrollFrame->GetWritingMode(); + nsRect physicalBounding = + FindScrollAnchoringBoundingRect(aScrollFrame, aCandidate); + LogicalRect logicalBounding(writingMode, physicalBounding, + aScrollFrame->mScrolledFrame->GetSize()); + return logicalBounding.BStart(writingMode); +} + +bool ScrollAnchorContainer::CanMaintainAnchor() const { + if (!StaticPrefs::layout_css_scroll_anchoring_enabled()) { + return false; + } + + // If we've been disabled due to heuristics, we don't anchor anymore. + if (mDisabled) { + return false; + } + + const nsStyleDisplay& disp = *Frame()->StyleDisplay(); + // Don't select a scroll anchor if the scroll frame has `overflow-anchor: + // none`. + if (disp.mOverflowAnchor != mozilla::StyleOverflowAnchor::Auto) { + return false; + } + + // Or if the scroll frame has not been scrolled from the logical origin. This + // is not in the specification [1], but Blink does this. + // + // [1] https://github.com/w3c/csswg-drafts/issues/3319 + if (Frame()->GetLogicalScrollPosition() == nsPoint()) { + return false; + } + + // Or if there is perspective that could affect the scrollable overflow rect + // for descendant frames. This is not in the specification as Blink doesn't + // share this behavior with perspective [1]. + // + // [1] https://github.com/w3c/csswg-drafts/issues/3322 + if (Frame()->ChildrenHavePerspective()) { + return false; + } + + return true; +} + +void ScrollAnchorContainer::SelectAnchor() { + MOZ_ASSERT(Frame()->mScrolledFrame); + MOZ_ASSERT(mAnchorNodeIsDirty); + + AUTO_PROFILER_LABEL("ScrollAnchorContainer::SelectAnchor", LAYOUT); + ANCHOR_LOG("Selecting anchor with scroll-port=%s.\n", + mozilla::ToString(Frame()->GetVisualOptimalViewingRect()).c_str()); + + // Select a new scroll anchor + nsIFrame* oldAnchor = mAnchorNode; + if (CanMaintainAnchor()) { + MOZ_DIAGNOSTIC_ASSERT( + !Frame()->mScrolledFrame->IsInScrollAnchorChain(), + "Our scrolled frame can't serve as or contain an anchor for an " + "ancestor if it can maintain its own anchor"); + ANCHOR_LOG("Beginning selection.\n"); + mAnchorNode = FindAnchorIn(Frame()->mScrolledFrame); + } else { + ANCHOR_LOG("Skipping selection, doesn't maintain a scroll anchor.\n"); + mAnchorNode = nullptr; + } + mAnchorMightBeSubOptimal = + mAnchorNode && mAnchorNode->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); + + // Update the anchor flags if needed + if (oldAnchor != mAnchorNode) { + ANCHOR_LOG("Anchor node has changed from (%p) to (%p).\n", oldAnchor, + mAnchorNode); + + // Unset all flags for the old scroll anchor + if (oldAnchor) { + SetAnchorFlags(Frame()->mScrolledFrame, oldAnchor, false); + } + + // Set all flags for the new scroll anchor + if (mAnchorNode) { + // Anchor selection will never select a descendant of a nested scroll + // frame which maintains an anchor, so we can set flags without + // conflicting with other scroll anchor containers. + SetAnchorFlags(Frame()->mScrolledFrame, mAnchorNode, true); + } + } else { + ANCHOR_LOG("Anchor node has remained (%p).\n", mAnchorNode); + } + + // Calculate the position to use for scroll adjustments + if (mAnchorNode) { + mLastAnchorOffset = FindScrollAnchoringBoundingOffset(Frame(), mAnchorNode); + ANCHOR_LOG("Using last anchor offset = %d.\n", mLastAnchorOffset); + } else { + mLastAnchorOffset = 0; + } + + mAnchorNodeIsDirty = false; +} + +void ScrollAnchorContainer::UserScrolled() { + if (mApplyingAnchorAdjustment) { + return; + } + InvalidateAnchor(); + + if (!StaticPrefs:: + layout_css_scroll_anchoring_reset_heuristic_during_animation() && + Frame()->ScrollAnimationState().contains( + nsIScrollableFrame::AnimationState::APZInProgress)) { + // We'd want to skip resetting our heuristic while APZ is running an async + // scroll because this UserScrolled function gets called on every refresh + // driver's tick during running the async scroll, thus it will clobber the + // heuristic. + return; + } + + mHeuristic.Reset(); +} + +void ScrollAnchorContainer::DisablingHeuristic::Reset() { + mConsecutiveScrollAnchoringAdjustments = SaturateUint32(0); + mConsecutiveScrollAnchoringAdjustmentLength = 0; + mTimeStamp = {}; +} + +void ScrollAnchorContainer::AdjustmentMade(nscoord aAdjustment) { + MOZ_ASSERT(!mDisabled, "How?"); + mDisabled = mHeuristic.AdjustmentMade(*this, aAdjustment); +} + +bool ScrollAnchorContainer::DisablingHeuristic::AdjustmentMade( + const ScrollAnchorContainer& aAnchor, nscoord aAdjustment) { + // A reasonably large number of times that we want to check for this. If we + // haven't hit this limit after these many attempts we assume we'll never hit + // it. + // + // This is to prevent the number getting too large and making the limit round + // to zero by mere precision error. + // + // 100k should be enough for anyone :) + static const uint32_t kAnchorCheckCountLimit = 100000; + + // Zero-length adjustments are common & don't have side effects, so we don't + // want them to consider them here; they'd bias our average towards 0. + MOZ_ASSERT(aAdjustment, "Don't call this API for zero-length adjustments"); + + const uint32_t maxConsecutiveAdjustments = + StaticPrefs::layout_css_scroll_anchoring_max_consecutive_adjustments(); + + if (!maxConsecutiveAdjustments) { + return false; + } + + // We don't high resolution for this timestamp. + const auto now = TimeStamp::NowLoRes(); + if (mConsecutiveScrollAnchoringAdjustments++ == 0) { + MOZ_ASSERT(mTimeStamp.IsNull()); + mTimeStamp = now; + } else if ( + const auto timeoutMs = StaticPrefs:: + layout_css_scroll_anchoring_max_consecutive_adjustments_timeout_ms(); + timeoutMs && (now - mTimeStamp).ToMilliseconds() > timeoutMs) { + Reset(); + return false; + } + + mConsecutiveScrollAnchoringAdjustmentLength = NSCoordSaturatingAdd( + mConsecutiveScrollAnchoringAdjustmentLength, aAdjustment); + + uint32_t consecutiveAdjustments = + mConsecutiveScrollAnchoringAdjustments.value(); + if (consecutiveAdjustments < maxConsecutiveAdjustments || + consecutiveAdjustments > kAnchorCheckCountLimit) { + return false; + } + + auto cssPixels = + CSSPixel::FromAppUnits(mConsecutiveScrollAnchoringAdjustmentLength); + double average = double(cssPixels) / consecutiveAdjustments; + uint32_t minAverage = StaticPrefs:: + layout_css_scroll_anchoring_min_average_adjustment_threshold(); + if (MOZ_LIKELY(std::abs(average) >= double(minAverage))) { + return false; + } + + ANCHOR_LOG_WITH(&aAnchor, + "Disabled scroll anchoring for container: " + "%f average, %f total out of %u consecutive adjustments\n", + average, float(cssPixels), consecutiveAdjustments); + + AutoTArray arguments; + arguments.AppendElement()->AppendInt(consecutiveAdjustments); + arguments.AppendElement()->AppendFloat(average); + arguments.AppendElement()->AppendFloat(cssPixels); + + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Layout"_ns, + aAnchor.Frame()->PresContext()->Document(), + nsContentUtils::eLAYOUT_PROPERTIES, + "ScrollAnchoringDisabledInContainer", + arguments); + return true; +} + +void ScrollAnchorContainer::SuppressAdjustments() { + ANCHOR_LOG("Received a scroll anchor suppression for %p.\n", this); + mSuppressAnchorAdjustment = true; + + // Forward to our parent if appropriate, that is, if we don't maintain an + // anchor, and we can't maintain one. + // + // Note that we need to check !CanMaintainAnchor(), instead of just whether + // our frame is in the anchor chain of our ancestor as InvalidateAnchor() + // does, given some suppression triggers apply even for nodes that are not in + // the anchor chain. + if (!mAnchorNode && !CanMaintainAnchor()) { + if (ScrollAnchorContainer* container = FindFor(Frame())) { + ANCHOR_LOG(" > Forwarding to parent anchor\n"); + container->SuppressAdjustments(); + } + } +} + +void ScrollAnchorContainer::InvalidateAnchor(ScheduleSelection aSchedule) { + ANCHOR_LOG("Invalidating scroll anchor %p for %p.\n", mAnchorNode, this); + + if (mAnchorNode) { + SetAnchorFlags(Frame()->mScrolledFrame, mAnchorNode, false); + } else if (Frame()->mScrolledFrame->IsInScrollAnchorChain()) { + ANCHOR_LOG(" > Forwarding to parent anchor\n"); + // We don't maintain an anchor, and our scrolled frame is in the anchor + // chain of an ancestor. Invalidate that anchor. + // + // NOTE: Intentionally not forwarding aSchedule: Scheduling is always safe + // and not doing so is just an optimization. + FindFor(Frame())->InvalidateAnchor(); + } + mAnchorNode = nullptr; + mAnchorMightBeSubOptimal = false; + mAnchorNodeIsDirty = true; + mLastAnchorOffset = 0; + + if (!CanMaintainAnchor() || aSchedule == ScheduleSelection::No) { + return; + } + + Frame()->PresShell()->PostPendingScrollAnchorSelection(this); +} + +void ScrollAnchorContainer::Destroy() { + InvalidateAnchor(ScheduleSelection::No); +} + +void ScrollAnchorContainer::ApplyAdjustments() { + if (!mAnchorNode || mAnchorNodeIsDirty || mDisabled || + Frame()->HasPendingScrollRestoration() || + (StaticPrefs:: + layout_css_scroll_anchoring_reset_heuristic_during_animation() && + Frame()->IsProcessingScrollEvent()) || + Frame()->ScrollAnimationState().contains( + nsIScrollableFrame::AnimationState::TriggeredByScript) || + Frame()->GetScrollPosition() == nsPoint()) { + ANCHOR_LOG( + "Ignoring post-reflow (anchor=%p, dirty=%d, disabled=%d, " + "pendingRestoration=%d, scrollevent=%d, scriptAnimating=%d, " + "zeroScrollPos=%d pendingSuppression=%d, " + "container=%p).\n", + mAnchorNode, mAnchorNodeIsDirty, mDisabled, + Frame()->HasPendingScrollRestoration(), + Frame()->IsProcessingScrollEvent(), + Frame()->ScrollAnimationState().contains( + nsIScrollableFrame::AnimationState::TriggeredByScript), + Frame()->GetScrollPosition() == nsPoint(), mSuppressAnchorAdjustment, + this); + if (mSuppressAnchorAdjustment) { + mSuppressAnchorAdjustment = false; + InvalidateAnchor(); + } + return; + } + + nscoord current = FindScrollAnchoringBoundingOffset(Frame(), mAnchorNode); + nscoord logicalAdjustment = current - mLastAnchorOffset; + WritingMode writingMode = Frame()->GetWritingMode(); + + ANCHOR_LOG("Anchor has moved from %d to %d.\n", mLastAnchorOffset, current); + + auto maybeInvalidate = MakeScopeExit([&] { + if (mAnchorMightBeSubOptimal && + StaticPrefs::layout_css_scroll_anchoring_reselect_if_suboptimal()) { + ANCHOR_LOG( + "Anchor might be suboptimal, invalidating to try finding a better " + "one\n"); + InvalidateAnchor(); + } + }); + + if (logicalAdjustment == 0) { + ANCHOR_LOG("Ignoring zero delta anchor adjustment for %p.\n", this); + mSuppressAnchorAdjustment = false; + return; + } + + if (mSuppressAnchorAdjustment) { + ANCHOR_LOG("Applying anchor adjustment suppression for %p.\n", this); + mSuppressAnchorAdjustment = false; + InvalidateAnchor(); + return; + } + + ANCHOR_LOG("Applying anchor adjustment of %d in %s with anchor %p.\n", + logicalAdjustment, ToString(writingMode).c_str(), mAnchorNode); + + AdjustmentMade(logicalAdjustment); + + nsPoint physicalAdjustment; + switch (writingMode.GetBlockDir()) { + case WritingMode::eBlockTB: { + physicalAdjustment.y = logicalAdjustment; + break; + } + case WritingMode::eBlockLR: { + physicalAdjustment.x = logicalAdjustment; + break; + } + case WritingMode::eBlockRL: { + physicalAdjustment.x = -logicalAdjustment; + break; + } + } + + MOZ_RELEASE_ASSERT(!mApplyingAnchorAdjustment); + // We should use AutoRestore here, but that doesn't work with bitfields + mApplyingAnchorAdjustment = true; + Frame()->ScrollToInternal(Frame()->GetScrollPosition() + physicalAdjustment, + ScrollMode::Instant, ScrollOrigin::Relative); + mApplyingAnchorAdjustment = false; + + nsPresContext* pc = Frame()->PresContext(); + if (Frame()->mIsRoot) { + pc->PresShell()->RootScrollFrameAdjusted(physicalAdjustment.y); + } + + // The anchor position may not be in the same relative position after + // adjustment. Update ourselves so we have consistent state. + mLastAnchorOffset = FindScrollAnchoringBoundingOffset(Frame(), mAnchorNode); +} + +ScrollAnchorContainer::ExamineResult +ScrollAnchorContainer::ExamineAnchorCandidate(nsIFrame* aFrame) const { +#ifdef DEBUG_FRAME_DUMP + nsCString tag = aFrame->ListTag(); + ANCHOR_LOG("\tVisiting frame=%s (%p).\n", tag.get(), aFrame); +#else + ANCHOR_LOG("\t\tVisiting frame=%p.\n", aFrame); +#endif + bool isText = !!Text::FromNodeOrNull(aFrame->GetContent()); + bool isContinuation = !!aFrame->GetPrevContinuation(); + + if (isText && isContinuation) { + ANCHOR_LOG("\t\tExcluding continuation text node.\n"); + return ExamineResult::Exclude; + } + + // Check if the author has opted out of scroll anchoring for this frame + // and its descendants. + const nsStyleDisplay* disp = aFrame->StyleDisplay(); + if (disp->mOverflowAnchor == mozilla::StyleOverflowAnchor::None) { + ANCHOR_LOG("\t\tExcluding `overflow-anchor: none`.\n"); + return ExamineResult::Exclude; + } + + // Sticky positioned elements can move with the scroll frame, making them + // unsuitable scroll anchors. This isn't in the specification yet [1], but + // matches Blink's implementation. + // + // [1] https://github.com/w3c/csswg-drafts/issues/3319 + if (aFrame->IsStickyPositioned()) { + ANCHOR_LOG("\t\tExcluding `position: sticky`.\n"); + return ExamineResult::Exclude; + } + + // The frame for a
element has a non-zero area, but Blink treats them + // as if they have no area, so exclude them specially. + if (aFrame->IsBrFrame()) { + ANCHOR_LOG("\t\tExcluding
.\n"); + return ExamineResult::Exclude; + } + + // Exclude frames that aren't accessible to content. + bool isChrome = + aFrame->GetContent() && aFrame->GetContent()->ChromeOnlyAccess(); + bool isPseudo = aFrame->Style()->IsPseudoElement(); + if (isChrome && !isPseudo) { + ANCHOR_LOG("\t\tExcluding chrome only content.\n"); + return ExamineResult::Exclude; + } + + const bool isReplaced = aFrame->IsFrameOfType(nsIFrame::eReplaced); + + const bool isNonReplacedInline = + aFrame->StyleDisplay()->IsInlineInsideStyle() && !isReplaced; + + const bool isAnonBox = aFrame->Style()->IsAnonBox(); + + // See if this frame has or could maintain its own anchor node. + const bool isScrollableWithAnchor = [&] { + nsIScrollableFrame* scrollable = do_QueryFrame(aFrame); + if (!scrollable) { + return false; + } + auto* anchor = scrollable->Anchor(); + return anchor->AnchorNode() || anchor->CanMaintainAnchor(); + }(); + + // We don't allow scroll anchors to be selected inside of nested scrollable + // frames which maintain an anchor node as it's not clear how an anchor + // adjustment should apply to multiple scrollable frames. + // + // It is important to descend into _some_ scrollable frames, specially + // overflow: hidden, as those don't generally maintain their own anchors, and + // it is a common case in the wild where scroll anchoring ought to work. + // + // We also don't allow scroll anchors to be selected inside of replaced + // elements (like ,