From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- layout/style/AnimatedPropertyID.h | 94 + layout/style/AnimatedPropertyIDSet.h | 149 + layout/style/AnimationCollection.cpp | 79 + layout/style/AnimationCollection.h | 71 + layout/style/AnimationCommon.h | 225 + layout/style/AttributeStyles.cpp | 112 + layout/style/AttributeStyles.h | 86 + layout/style/BuiltinCounterStyleList.h | 38 + layout/style/CSS.cpp | 71 + layout/style/CSS.h | 47 + layout/style/CSSContainerRule.cpp | 91 + layout/style/CSSContainerRule.h | 55 + layout/style/CSSCounterStyleRule.cpp | 101 + layout/style/CSSCounterStyleRule.h | 63 + layout/style/CSSEnabledState.h | 38 + layout/style/CSSFontFaceRule.cpp | 223 + layout/style/CSSFontFaceRule.h | 104 + layout/style/CSSFontFeatureValuesRule.cpp | 84 + layout/style/CSSFontFeatureValuesRule.h | 57 + layout/style/CSSFontPaletteValuesRule.cpp | 74 + layout/style/CSSFontPaletteValuesRule.h | 59 + layout/style/CSSImportRule.cpp | 156 + layout/style/CSSImportRule.h | 63 + layout/style/CSSKeyframeRule.cpp | 220 + layout/style/CSSKeyframeRule.h | 62 + layout/style/CSSKeyframesRule.cpp | 364 + layout/style/CSSKeyframesRule.h | 70 + layout/style/CSSLayerBlockRule.cpp | 66 + layout/style/CSSLayerBlockRule.h | 48 + layout/style/CSSLayerStatementRule.cpp | 68 + layout/style/CSSLayerStatementRule.h | 49 + layout/style/CSSMediaRule.cpp | 113 + layout/style/CSSMediaRule.h | 55 + layout/style/CSSMozDocumentRule.cpp | 143 + layout/style/CSSMozDocumentRule.h | 53 + layout/style/CSSNamespaceRule.cpp | 51 + layout/style/CSSNamespaceRule.h | 63 + layout/style/CSSPageRule.cpp | 198 + layout/style/CSSPageRule.h | 108 + layout/style/CSSPropFlags.h | 68 + layout/style/CSSPropertyRule.cpp | 72 + layout/style/CSSPropertyRule.h | 61 + layout/style/CSSRuleList.cpp | 31 + layout/style/CSSRuleList.h | 39 + layout/style/CSSStyleRule.cpp | 323 + layout/style/CSSStyleRule.h | 125 + layout/style/CSSSupportsRule.cpp | 76 + layout/style/CSSSupportsRule.h | 47 + layout/style/CSSValue.h | 54 + layout/style/CachedInheritingStyles.cpp | 70 + layout/style/CachedInheritingStyles.h | 66 + layout/style/ComputedStyle.cpp | 433 + layout/style/ComputedStyle.h | 354 + layout/style/ComputedStyleInlines.h | 132 + layout/style/CounterStyleManager.cpp | 1884 +++ layout/style/CounterStyleManager.h | 355 + layout/style/DeclarationBlock.cpp | 32 + layout/style/DeclarationBlock.h | 245 + layout/style/DocumentMatchingFunction.h | 30 + layout/style/DocumentStyleRootIterator.cpp | 47 + layout/style/DocumentStyleRootIterator.h | 45 + layout/style/ErrorReporter.cpp | 277 + layout/style/ErrorReporter.h | 73 + layout/style/FontFace.cpp | 314 + layout/style/FontFace.h | 126 + layout/style/FontFaceImpl.cpp | 840 ++ layout/style/FontFaceImpl.h | 308 + layout/style/FontFaceSet.cpp | 487 + layout/style/FontFaceSet.h | 174 + layout/style/FontFaceSetDocumentImpl.cpp | 763 + layout/style/FontFaceSetDocumentImpl.h | 123 + layout/style/FontFaceSetImpl.cpp | 954 ++ layout/style/FontFaceSetImpl.h | 315 + layout/style/FontFaceSetIterator.cpp | 82 + layout/style/FontFaceSetIterator.h | 42 + layout/style/FontFaceSetWorkerImpl.cpp | 374 + layout/style/FontFaceSetWorkerImpl.h | 68 + layout/style/FontLoaderUtils.cpp | 185 + layout/style/FontLoaderUtils.h | 66 + layout/style/FontPreloader.cpp | 32 + layout/style/FontPreloader.h | 30 + layout/style/GeckoBindings.cpp | 1819 +++ layout/style/GeckoBindings.h | 660 + layout/style/GenerateCSSPropertyID.py | 40 + layout/style/GenerateCSSPropsGenerated.py | 114 + .../GenerateCompositorAnimatableProperties.py | 35 + layout/style/GenerateComputedDOMStyleGenerated.py | 81 + layout/style/GenerateCountedUnknownProperties.py | 24 + layout/style/GenerateServoCSSPropList.py | 138 + layout/style/GlobalStyleSheetCache.cpp | 561 + layout/style/GlobalStyleSheetCache.h | 132 + layout/style/GroupRule.cpp | 147 + layout/style/GroupRule.h | 96 + layout/style/ImageDocument.css | 43 + layout/style/ImageLoader.cpp | 834 ++ layout/style/ImageLoader.h | 170 + layout/style/ImportScanner.cpp | 235 + layout/style/ImportScanner.h | 94 + layout/style/LayerAnimationInfo.cpp | 46 + layout/style/LayerAnimationInfo.h | 91 + layout/style/Loader.cpp | 2344 +++ layout/style/Loader.h | 665 + layout/style/MappedDeclarationsBuilder.cpp | 42 + layout/style/MappedDeclarationsBuilder.h | 220 + layout/style/MediaFeatureChange.h | 91 + layout/style/MediaList.cpp | 176 + layout/style/MediaList.h | 101 + layout/style/MediaQueryList.cpp | 147 + layout/style/MediaQueryList.h | 107 + layout/style/PaintWorkletGlobalScope.cpp | 35 + layout/style/PaintWorkletGlobalScope.h | 38 + layout/style/PaintWorkletImpl.cpp | 40 + layout/style/PaintWorkletImpl.h | 36 + layout/style/PostTraversalTask.cpp | 58 + layout/style/PostTraversalTask.h | 128 + layout/style/PreferenceSheet.cpp | 341 + layout/style/PreferenceSheet.h | 135 + layout/style/PreloadedStyleSheet.cpp | 93 + layout/style/PreloadedStyleSheet.h | 75 + layout/style/PseudoStyleType.cpp | 39 + layout/style/PseudoStyleType.h | 113 + layout/style/Rule.cpp | 130 + layout/style/Rule.h | 141 + layout/style/RustCell.h | 45 + layout/style/ServoBindingTypes.h | 161 + layout/style/ServoBindings.h | 132 + layout/style/ServoBindings.toml | 691 + layout/style/ServoBoxedTypeList.h | 32 + layout/style/ServoCSSParser.cpp | 90 + layout/style/ServoCSSParser.h | 159 + layout/style/ServoCSSPropList.mako.py | 169 + layout/style/ServoCSSRuleList.cpp | 306 + layout/style/ServoCSSRuleList.h | 96 + layout/style/ServoComputedData.h | 111 + layout/style/ServoElementSnapshot.cpp | 85 + layout/style/ServoElementSnapshot.h | 171 + layout/style/ServoElementSnapshotTable.h | 23 + layout/style/ServoLockedArcTypeList.h | 25 + layout/style/ServoStyleConstsForwards.h | 236 + layout/style/ServoStyleConstsInlines.h | 1202 ++ layout/style/ServoStyleSet.cpp | 1579 +++ layout/style/ServoStyleSet.h | 745 + layout/style/ServoStyleSetInlines.h | 27 + layout/style/ServoTraversalStatistics.h | 31 + layout/style/ServoTypes.h | 141 + layout/style/ServoUtils.h | 38 + layout/style/ShadowParts.cpp | 142 + layout/style/ShadowParts.h | 47 + layout/style/SharedStyleSheetCache.cpp | 222 + layout/style/SharedStyleSheetCache.h | 80 + layout/style/SharedSubResourceCache.h | 507 + layout/style/SheetLoadData.h | 286 + layout/style/SheetParsingMode.h | 49 + layout/style/StreamLoader.cpp | 212 + layout/style/StreamLoader.h | 64 + layout/style/StyleAnimationValue.cpp | 284 + layout/style/StyleAnimationValue.h | 138 + layout/style/StyleColor.cpp | 81 + layout/style/StyleColorInlines.h | 78 + layout/style/StylePreloadKind.h | 34 + layout/style/StyleSheet.cpp | 1484 ++ layout/style/StyleSheet.h | 615 + layout/style/StyleSheetInfo.h | 92 + layout/style/StyleSheetInlines.h | 43 + layout/style/TimelineCollection.cpp | 67 + layout/style/TimelineCollection.h | 70 + layout/style/TimelineManager.cpp | 187 + layout/style/TimelineManager.h | 86 + layout/style/TopLevelImageDocument.css | 49 + layout/style/TopLevelVideoDocument.css | 32 + layout/style/URLExtraData.cpp | 52 + layout/style/URLExtraData.h | 87 + layout/style/UserAgentStyleSheetID.h | 23 + layout/style/UserAgentStyleSheetList.h | 34 + layout/style/contenteditable.css | 315 + layout/style/crashtests/1017798-1.css | 84 + layout/style/crashtests/1017798-1.html | 126 + layout/style/crashtests/1028514-1.html | 18 + layout/style/crashtests/105619-1.html | 33 + layout/style/crashtests/1066089-1.html | 21 + layout/style/crashtests/1074651-1.html | 4 + layout/style/crashtests/1089463-1.html | 20 + layout/style/crashtests/1135534.html | 1 + layout/style/crashtests/1136010-1.html | 16 + layout/style/crashtests/1146101-1.html | 10 + layout/style/crashtests/1153693-1.html | 22 + layout/style/crashtests/1156969.svg | 8 + layout/style/crashtests/1161320-1.html | 25 + layout/style/crashtests/1161320-2.html | 25 + layout/style/crashtests/1161366-1.html | 7 + layout/style/crashtests/1163446-1.html | 4 + layout/style/crashtests/1164813-1.html | 33 + layout/style/crashtests/1167782-1.html | 11 + layout/style/crashtests/1186768-1.xhtml | 10 + layout/style/crashtests/1200568-1.html | 16 + layout/style/crashtests/1206105-1.html | 6 + layout/style/crashtests/1223688-1.html | 19 + layout/style/crashtests/1223694-1.html | 17 + layout/style/crashtests/1226400-1.html | 55 + layout/style/crashtests/1227498.html | 26 + layout/style/crashtests/1227501-1.html | 8 + layout/style/crashtests/1228789-1.html | 4 + layout/style/crashtests/1230408-1.html | 8 + layout/style/crashtests/1233135-1.html | 13 + layout/style/crashtests/1233135-2.html | 11 + layout/style/crashtests/1236398.xhtml | 71 + layout/style/crashtests/1238660-1.html | 19 + layout/style/crashtests/1245260-1.html | 53 + layout/style/crashtests/1247865-1.html | 19 + layout/style/crashtests/1250791.html | 8 + layout/style/crashtests/1264396-1.html | 14 + layout/style/crashtests/1264949.html | 23 + layout/style/crashtests/1265611-1.html | 24 + layout/style/crashtests/1270795.html | 15 + layout/style/crashtests/1275026.html | 4 + layout/style/crashtests/1277908-1.html | 26 + layout/style/crashtests/1277908-2.html | 19 + layout/style/crashtests/1278463-1.html | 21 + layout/style/crashtests/1279819-1.html | 18 + layout/style/crashtests/1282076-1.html | 51 + layout/style/crashtests/1282076-2.html | 46 + layout/style/crashtests/1290994-1.html | 11 + layout/style/crashtests/1290994-2.html | 11 + layout/style/crashtests/1290994-3.html | 11 + layout/style/crashtests/1290994-4.html | 8 + layout/style/crashtests/1314531.html | 2 + layout/style/crashtests/1315889-1.html | 12 + layout/style/crashtests/1315894-1.html | 9 + layout/style/crashtests/1319072-1.html | 20 + layout/style/crashtests/1320423-1.html | 22 + layout/style/crashtests/1321357-1.html | 12 + layout/style/crashtests/1328535-1.html | 17 + layout/style/crashtests/1331272.html | 16 + layout/style/crashtests/1332550.html | 19 + layout/style/crashtests/1333001-1.css | 1 + layout/style/crashtests/1333001-1.html | 9 + layout/style/crashtests/1340248.html | 14 + layout/style/crashtests/1340344.html | 15 + layout/style/crashtests/1342316-1.html | 20 + layout/style/crashtests/1344210.html | 22 + layout/style/crashtests/1353312.html | 14 + layout/style/crashtests/1356601-1.html | 18 + layout/style/crashtests/1364139-1.html | 20 + layout/style/crashtests/1371450-1.html | 34 + layout/style/crashtests/1374175-1.html | 12 + layout/style/crashtests/1375812-1.html | 25 + layout/style/crashtests/1377053-1.html | 10 + layout/style/crashtests/1377256-1-helper.html | 13 + layout/style/crashtests/1377256-1.html | 9 + layout/style/crashtests/1378064-1.html | 38 + layout/style/crashtests/1378814.html | 4 + layout/style/crashtests/1380800.html | 5 + layout/style/crashtests/1381420-1.html | 35 + layout/style/crashtests/1381682.html | 18 + layout/style/crashtests/1382672.html | 11 + layout/style/crashtests/1382710.html | 8 + layout/style/crashtests/1383001-2.html | 15 + layout/style/crashtests/1383001.html | 23 + layout/style/crashtests/1383319.html | 18 + layout/style/crashtests/1383493-1.html | 10 + layout/style/crashtests/1383589-1.html | 14 + layout/style/crashtests/1383975.html | 10 + layout/style/crashtests/1383981-2.html | 21 + layout/style/crashtests/1383981-3.html | 36 + layout/style/crashtests/1383981.html | 22 + layout/style/crashtests/1384232.html | 9 + layout/style/crashtests/1384824-1.html | 27 + layout/style/crashtests/1384824-2.html | 31 + layout/style/crashtests/1386773.html | 17 + layout/style/crashtests/1387481-1-iframe.html | 26 + layout/style/crashtests/1387481-1.html | 13 + layout/style/crashtests/1387499.html | 15 + layout/style/crashtests/1388234.html | 9 + layout/style/crashtests/1389645.html | 13 + layout/style/crashtests/1390726.html | 27 + layout/style/crashtests/1391577.html | 14 + layout/style/crashtests/1393189.html | 13 + layout/style/crashtests/1393580.html | 10 + layout/style/crashtests/1393791.html | 12 + layout/style/crashtests/1395719.html | 19 + layout/style/crashtests/1395725.html | 15 + layout/style/crashtests/1396041.html | 9 + layout/style/crashtests/1397091.html | 13 + layout/style/crashtests/1397363-1.html | 13 + layout/style/crashtests/1397439-1.html | 6 + layout/style/crashtests/1398479.html | 1 + layout/style/crashtests/1398581.html | 17 + layout/style/crashtests/1399006.html | 29 + layout/style/crashtests/1399546.html | 17 + layout/style/crashtests/1400035.html | 18 + layout/style/crashtests/1400325.html | 6 + layout/style/crashtests/1400926.html | 10 + layout/style/crashtests/1400936-1.html | 26 + layout/style/crashtests/1400936-2.html | 12 + layout/style/crashtests/1401256.html | 5 + layout/style/crashtests/1401706.html | 10 + layout/style/crashtests/1401801.html | 24 + layout/style/crashtests/1401825.html | 7 + layout/style/crashtests/1402218-1.html | 15 + layout/style/crashtests/1402366.html | 10 + layout/style/crashtests/1402419.html | 3 + layout/style/crashtests/1402472.html | 13 + layout/style/crashtests/1403028.html | 9 + layout/style/crashtests/1403433.html | 6 + layout/style/crashtests/1403465.html | 24 + layout/style/crashtests/1403592.html | 19 + layout/style/crashtests/1403615.html | 24 + layout/style/crashtests/1403712.html | 26 + layout/style/crashtests/1404057.html | 6 + layout/style/crashtests/1404180-1.html | 22 + layout/style/crashtests/1404316.html | 20 + layout/style/crashtests/1404324-1.html | 12 + layout/style/crashtests/1404324-2.html | 10 + layout/style/crashtests/1404324-3.html | 14 + layout/style/crashtests/1405880.html | 21 + layout/style/crashtests/1406222-1.html | 25 + layout/style/crashtests/1406222-2.html | 15 + layout/style/crashtests/1409183.html | 15 + layout/style/crashtests/1409502.html | 13 + layout/style/crashtests/1409931.html | 14 + layout/style/crashtests/1410226-1.html | 8 + layout/style/crashtests/1410226-2.html | 16 + layout/style/crashtests/1411008.html | 22 + layout/style/crashtests/1411143.html | 7 + layout/style/crashtests/1411478.html | 6 + layout/style/crashtests/1413288.html | 17 + layout/style/crashtests/1413361.html | 8 + layout/style/crashtests/1413670.html | 17 + layout/style/crashtests/1415353.html | 8 + layout/style/crashtests/1418059.html | 24 + layout/style/crashtests/1418867.html | 33 + layout/style/crashtests/1419554.html | 9 + layout/style/crashtests/1426312.html | 4 + layout/style/crashtests/1439793.html | 10 + layout/style/crashtests/1445682.html | 16 + layout/style/crashtests/1449243.html | 13 + layout/style/crashtests/1450691.html | 12 + layout/style/crashtests/1453206.html | 8 + layout/style/crashtests/1454140.html | 4 + layout/style/crashtests/1455108.html | 17 + layout/style/crashtests/1457288.html | 9 + layout/style/crashtests/1457985.html | 2 + layout/style/crashtests/1468640.html | 10 + layout/style/crashtests/1469076.html | 12 + layout/style/crashtests/1475003.html | 21 + layout/style/crashtests/1479681.html | 14 + layout/style/crashtests/1488817.html | 27 + layout/style/crashtests/1490012.html | 11 + layout/style/crashtests/1502893.html | 29 + layout/style/crashtests/1507674.html | 16 + layout/style/crashtests/1509989.html | 11 + layout/style/crashtests/1514086.html | 13 + layout/style/crashtests/1533783.html | 10 + layout/style/crashtests/1533891.html | 13 + layout/style/crashtests/1533968.html | 22 + layout/style/crashtests/1545177.html | 15 + layout/style/crashtests/1546255.html | 29 + layout/style/crashtests/1552911.html | 15 + layout/style/crashtests/1562361.html | 15 + layout/style/crashtests/1566684.html | 16 + layout/style/crashtests/1579788.html | 15 + layout/style/crashtests/1580307.html | 1 + layout/style/crashtests/1581579.html | 3 + layout/style/crashtests/1586444.html | 15 + layout/style/crashtests/1593766.html | 3 + layout/style/crashtests/1594949.html | 13 + layout/style/crashtests/1594960.html | 15 + layout/style/crashtests/1599286.html | 2 + layout/style/crashtests/1609786.html | 9 + layout/style/crashtests/1616407.html | 7 + layout/style/crashtests/1616433.html | 9 + layout/style/crashtests/1639533.html | 15 + layout/style/crashtests/1640040.html | 11 + layout/style/crashtests/1806189-1.html | 15 + layout/style/crashtests/1821416.html | 21 + layout/style/crashtests/1872309.html | 18 + layout/style/crashtests/187671-1.html | 12 + layout/style/crashtests/192408-1.html | 15 + layout/style/crashtests/285727-1.html | 13 + layout/style/crashtests/286707-1.html | 2 + layout/style/crashtests/317561-1.html | 104 + layout/style/crashtests/330998-1.html | 30 + layout/style/crashtests/363950.html | 20 + layout/style/crashtests/368175-1.html | 14 + layout/style/crashtests/368740.html | 25 + layout/style/crashtests/379788-1.html | 9 + layout/style/crashtests/383979-1.xhtml | 31 + layout/style/crashtests/383979-2.html | 35 + layout/style/crashtests/386939-1.html | 24 + layout/style/crashtests/391034-1.xhtml | 17 + layout/style/crashtests/397022-1.html | 17 + layout/style/crashtests/399289-1.svg | 3 + layout/style/crashtests/404470-1.html | 15 + layout/style/crashtests/411603-1.html | 7 + layout/style/crashtests/412588-1.html | 5 + layout/style/crashtests/413274-1.xhtml | 18 + layout/style/crashtests/416461-1.xhtml | 6 + layout/style/crashtests/418007-1.xhtml | 24 + layout/style/crashtests/431705-1.xhtml | 6 + layout/style/crashtests/432561-1.html | 17 + layout/style/crashtests/437170-1.html | 23 + layout/style/crashtests/437532-1.html | 12 + layout/style/crashtests/439184-1.html | 31 + layout/style/crashtests/444237-1.html | 1 + layout/style/crashtests/444848-1.html | 9 + layout/style/crashtests/447776-1.html | 7 + layout/style/crashtests/447783-1.html | 8 + layout/style/crashtests/448161-1.html | 22 + layout/style/crashtests/448161-2.html | 9 + layout/style/crashtests/452150-1.xhtml | 6 + layout/style/crashtests/456196.html | 16 + layout/style/crashtests/460209-1.html | 9 + layout/style/crashtests/460217-1.html | 15 + layout/style/crashtests/460323-1.html | 30 + layout/style/crashtests/466845-1.html | 14 + layout/style/crashtests/469432-1.xhtml | 8 + layout/style/crashtests/472195-1.html | 13 + layout/style/crashtests/472237-1.html | 26 + layout/style/crashtests/473720-1.html | 15 + layout/style/crashtests/473892-1.html | 12 + layout/style/crashtests/473914-1.html | 23 + layout/style/crashtests/474377-1.xhtml | 18 + layout/style/crashtests/478321-1.xhtml | 1 + layout/style/crashtests/481557.html | 9 + layout/style/crashtests/495269-1.html | 12 + layout/style/crashtests/495269-2.html | 12 + layout/style/crashtests/498036-1.html | 15 + layout/style/crashtests/509155-1.html | 4 + layout/style/crashtests/509156-1.html | 5 + layout/style/crashtests/509569-1.html | 2 + layout/style/crashtests/512851-1.xhtml | 23 + layout/style/crashtests/524252-1.html | 10 + layout/style/crashtests/536789-1.html | 11 + layout/style/crashtests/539613-1.xhtml | 5 + layout/style/crashtests/558943-1.xhtml | 11 + layout/style/crashtests/559491.html | 29 + layout/style/crashtests/565248-1.html | 2 + layout/style/crashtests/571105-1.xhtml | 1 + layout/style/crashtests/573127-1.html | 20 + layout/style/crashtests/575464-1.html | 1 + layout/style/crashtests/580685.html | 10 + layout/style/crashtests/585185-1.html | 1 + layout/style/crashtests/588627-1.html | 4 + layout/style/crashtests/592698-1.html | 29 + layout/style/crashtests/601437-1.html | 7 + layout/style/crashtests/601439-1.html | 8 + layout/style/crashtests/605689-1.html | 13 + layout/style/crashtests/611922-1.html | 13 + layout/style/crashtests/612213.html | 17 + layout/style/crashtests/621596-1.html | 18 + layout/style/crashtests/622314-1.xhtml | 26 + layout/style/crashtests/635153.html | 16 + layout/style/crashtests/637242.xhtml | 27 + layout/style/crashtests/645142.html | 11 + layout/style/crashtests/652976-1.svg | 10 + layout/style/crashtests/653675.html | 1 + layout/style/crashtests/665209-1.html | 16 + layout/style/crashtests/671799-1.html | 6 + layout/style/crashtests/671799-2.html | 17 + layout/style/crashtests/690990-1.html | 20 + layout/style/crashtests/694775.html | 7 + layout/style/crashtests/696188-1.html | 20 + layout/style/crashtests/696869-1.html | 2 + layout/style/crashtests/700116.html | 5 + layout/style/crashtests/729126-1.html | 10 + layout/style/crashtests/729126-2.html | 10 + layout/style/crashtests/786108-1.html | 22 + layout/style/crashtests/786108-2.html | 23 + layout/style/crashtests/788836.html | 3 + layout/style/crashtests/806310-1.html | 4 + layout/style/crashtests/809762.html | 22 + layout/style/crashtests/812824.html | 1 + layout/style/crashtests/822766-1.html | 31 + layout/style/crashtests/822877.html | 15 + layout/style/crashtests/827213-1.html | 9 + layout/style/crashtests/827220.html | 5 + layout/style/crashtests/827591-1.html | 19 + layout/style/crashtests/829817.html | 20 + layout/style/crashtests/842134.html | 1 + layout/style/crashtests/861489-1.html | 29 + layout/style/crashtests/862113.html | 16 + layout/style/crashtests/867487.html | 24 + layout/style/crashtests/873222.html | 17 + layout/style/crashtests/873260-1.html | 16 + layout/style/crashtests/873260-2.html | 16 + layout/style/crashtests/880862.html | 28 + layout/style/crashtests/894245-1.html | 4 + layout/style/crashtests/915440.html | 4 + layout/style/crashtests/927734-1.html | 10 + layout/style/crashtests/930270-1.html | 6 + layout/style/crashtests/930270-2.html | 9 + layout/style/crashtests/945048-1.html | 5 + layout/style/crashtests/972199-1.html | 37 + layout/style/crashtests/989965-1.html | 9 + layout/style/crashtests/992333-1.html | 10 + layout/style/crashtests/blue-32x32.png | Bin 0 -> 110 bytes .../crashtests/border-image-visited-link.html | 10 + .../crashtests/content-only-on-link-before.html | 5 + .../crashtests/content-only-on-visited-before.html | 5 + layout/style/crashtests/crashtests.list | 325 + .../style/crashtests/font-face-truncated-src.html | 2 + .../style/crashtests/large_border_image_width.html | 9 + .../style/crashtests/link-transition-before.html | 27 + .../crashtests/long-url-list-stack-overflow.html | 23 + .../crashtests/scale-on-block-continuation.html | 43 + layout/style/designmode.css | 8 + layout/style/extra-bindgen-flags.in | 1 + layout/style/jar.mn | 35 + layout/style/moz.build | 353 + layout/style/nsAnimationManager.cpp | 467 + layout/style/nsAnimationManager.h | 109 + layout/style/nsCSSAnonBoxList.h | 163 + layout/style/nsCSSAnonBoxes.cpp | 45 + layout/style/nsCSSAnonBoxes.h | 63 + layout/style/nsCSSCounterDescList.h | 16 + layout/style/nsCSSFontDescList.h | 20 + layout/style/nsCSSPropertyID.h.in | 87 + layout/style/nsCSSPropertyIDSet.h | 304 + layout/style/nsCSSProps.cpp | 247 + layout/style/nsCSSProps.h | 230 + layout/style/nsCSSPseudoElementList.h | 109 + layout/style/nsCSSPseudoElements.cpp | 154 + layout/style/nsCSSPseudoElements.h | 180 + layout/style/nsCSSValue.cpp | 122 + layout/style/nsCSSValue.h | 148 + layout/style/nsCSSVisitedDependentPropList.h | 36 + layout/style/nsComputedDOMStyle.cpp | 2434 ++++ layout/style/nsComputedDOMStyle.h | 400 + layout/style/nsDOMCSSAttrDeclaration.cpp | 278 + layout/style/nsDOMCSSAttrDeclaration.h | 101 + layout/style/nsDOMCSSDeclaration.cpp | 373 + layout/style/nsDOMCSSDeclaration.h | 187 + layout/style/nsDOMCSSValueList.cpp | 62 + layout/style/nsDOMCSSValueList.h | 39 + layout/style/nsFontFaceLoader.cpp | 379 + layout/style/nsFontFaceLoader.h | 74 + layout/style/nsFontFaceUtils.cpp | 231 + layout/style/nsFontFaceUtils.h | 22 + layout/style/nsICSSDeclaration.cpp | 28 + layout/style/nsICSSDeclaration.h | 127 + layout/style/nsICSSLoaderObserver.h | 48 + layout/style/nsMediaFeatures.cpp | 407 + layout/style/nsROCSSPrimitiveValue.cpp | 169 + layout/style/nsROCSSPrimitiveValue.h | 83 + layout/style/nsStyleAutoArray.h | 81 + layout/style/nsStyleConsts.h | 595 + layout/style/nsStyleStruct.cpp | 3604 +++++ layout/style/nsStyleStruct.h | 2085 +++ layout/style/nsStyleStructFwd.h | 79 + layout/style/nsStyleStructInlines.h | 141 + layout/style/nsStyleStructList.h | 68 + layout/style/nsStyleTransformMatrix.cpp | 653 + layout/style/nsStyleTransformMatrix.h | 190 + layout/style/nsStyleUtil.cpp | 388 + layout/style/nsStyleUtil.h | 175 + layout/style/nsTransitionManager.cpp | 497 + layout/style/nsTransitionManager.h | 87 + layout/style/res/Mozilla_Bullet.bf | 139 + layout/style/res/Mozilla_Bullet.ttf | 0 layout/style/res/Mozilla_Bullet.woff2 | 0 layout/style/res/accessiblecaret-normal.svg | 10 + layout/style/res/accessiblecaret-normal@1.5x.png | Bin 0 -> 1453 bytes layout/style/res/accessiblecaret-normal@1x.png | Bin 0 -> 999 bytes layout/style/res/accessiblecaret-normal@2.25x.png | Bin 0 -> 2053 bytes layout/style/res/accessiblecaret-normal@2x.png | Bin 0 -> 2118 bytes layout/style/res/accessiblecaret-tilt-left.svg | 10 + .../style/res/accessiblecaret-tilt-left@1.5x.png | Bin 0 -> 1443 bytes layout/style/res/accessiblecaret-tilt-left@1x.png | Bin 0 -> 993 bytes .../style/res/accessiblecaret-tilt-left@2.25x.png | Bin 0 -> 2092 bytes layout/style/res/accessiblecaret-tilt-left@2x.png | Bin 0 -> 1775 bytes layout/style/res/accessiblecaret-tilt-right.svg | 10 + .../style/res/accessiblecaret-tilt-right@1.5x.png | Bin 0 -> 1440 bytes layout/style/res/accessiblecaret-tilt-right@1x.png | Bin 0 -> 982 bytes .../style/res/accessiblecaret-tilt-right@2.25x.png | Bin 0 -> 2075 bytes layout/style/res/accessiblecaret-tilt-right@2x.png | Bin 0 -> 1766 bytes layout/style/res/accessiblecaret.css | 108 + layout/style/res/counterstyles.css | 365 + layout/style/res/details.css | 21 + layout/style/res/forms.css | 932 ++ layout/style/res/html.css | 927 ++ layout/style/res/noframes.css | 13 + layout/style/res/password-hide.svg | 7 + layout/style/res/password.svg | 7 + layout/style/res/plaintext.css | 36 + layout/style/res/quirk.css | 105 + layout/style/res/scrollbars.css | 234 + layout/style/res/searchfield-cancel.svg | 20 + layout/style/res/ua.css | 455 + layout/style/res/viewsource.css | 116 + layout/style/test/Ahem.ttf | Bin 0 -> 12480 bytes layout/style/test/BitPattern.woff | Bin 0 -> 6248 bytes layout/style/test/ListCSSProperties.cpp | 178 + layout/style/test/ParseCSS.cpp | 80 + layout/style/test/additional_sheets_helper.html | 7 + layout/style/test/animation_utils.js | 935 ++ layout/style/test/browser.toml | 19 + layout/style/test/browser_bug453896.js | 16 + layout/style/test/browser_sourcemap.js | 41 + layout/style/test/browser_sourcemap_comment.js | 47 + layout/style/test/browser_sourceurl_comment.js | 43 + layout/style/test/bug1382568-iframe.html | 8 + layout/style/test/bug1729861.js | 81 + layout/style/test/bug453896_iframe.html | 66 + layout/style/test/bug517224.sjs | 27 + layout/style/test/bug732209-css.sjs | 30 + layout/style/test/ccd-quirks.html | 129 + layout/style/test/ccd-standards.html | 128 + layout/style/test/ccd.sjs | 71 + layout/style/test/chrome/bug418986-2.js | 318 + layout/style/test/chrome/bug535806-css.css | 1 + layout/style/test/chrome/bug535806-html.html | 8 + layout/style/test/chrome/bug535806-xul.xhtml | 8 + .../style/test/chrome/chrome-only-media-queries.js | 34 + layout/style/test/chrome/chrome.toml | 51 + layout/style/test/chrome/display_mode.html | 122 + layout/style/test/chrome/display_mode_reflow.html | 84 + .../test/chrome/display_mode_reflow_iframe.html | 23 + layout/style/test/chrome/hover_empty.html | 4 + layout/style/test/chrome/hover_helper.html | 270 + layout/style/test/chrome/import_useless1.css | 3 + layout/style/test/chrome/import_useless2.css | 3 + layout/style/test/chrome/match.png | Bin 0 -> 1210 bytes layout/style/test/chrome/mismatch.png | Bin 0 -> 1573 bytes layout/style/test/chrome/moz_document_helper.html | 2 + layout/style/test/chrome/test_bug1157097.html | 27 + layout/style/test/chrome/test_bug1346623.html | 60 + layout/style/test/chrome/test_bug1371453.html | 33 + layout/style/test/chrome/test_bug418986-2.xhtml | 29 + layout/style/test/chrome/test_bug511909.html | 194 + layout/style/test/chrome/test_bug535806.xhtml | 43 + .../chrome/test_chrome_only_media_queries.html | 84 + ...onstructable_stylesheets_chrome_only_rules.html | 11 + layout/style/test/chrome/test_display_mode.html | 39 + .../test/chrome/test_display_mode_reflow.html | 41 + layout/style/test/chrome/test_hover.html | 29 + .../style/test/chrome/test_moz_document_rules.html | 97 + .../chrome/test_moz_document_serialization.html | 58 + .../test/chrome/test_scrollbar_inline_size.html | 36 + .../chrome/test_stylesheet_clone_import_rule.html | 86 + layout/style/test/css_properties_like_longhand.js | 1 + layout/style/test/descriptor_database.js | 142 + layout/style/test/empty.html | 1 + layout/style/test/file_animations_async_tests.html | 77 + layout/style/test/file_animations_omta_scroll.html | 392 + .../test/file_animations_omta_scroll_rtl.html | 112 + .../file_animations_with_disabled_properties.html | 50 + layout/style/test/file_bug1055933_circle-xxl.png | Bin 0 -> 4857 bytes layout/style/test/file_bug1089417_iframe.html | 17 + layout/style/test/file_bug1375944.html | 13 + layout/style/test/file_bug1381233.html | 4 + layout/style/test/file_bug1443344.css | 1 + layout/style/test/file_bug645998-1.css | 1 + layout/style/test/file_bug645998-2.css | 1 + layout/style/test/file_bug829816.css | Bin 0 -> 76 bytes .../file_computed_style_bfcache_display_none.html | 6 + .../file_computed_style_bfcache_display_none2.html | 6 + .../style/test/file_font_loading_api_vframe.html | 2 + layout/style/test/file_shared_sheet_caching.css | 3 + layout/style/test/file_shared_sheet_caching.html | 12 + ..._value_serialization_individual_transforms.html | 65 + layout/style/test/flexbox_layout_testcases.js | 1317 ++ layout/style/test/gen-css-properties.py | 24 + layout/style/test/gtest/ImportScannerTest.cpp | 119 + layout/style/test/gtest/StyloParsingBench.cpp | 116 + layout/style/test/gtest/example.css | 2925 ++++ .../test/gtest/generate_example_stylesheet.py | 16 + layout/style/test/gtest/moz.build | 24 + layout/style/test/mapped.css | 3 + layout/style/test/mapped.css^headers^ | 1 + layout/style/test/mapped2.css | 4 + layout/style/test/mapped2.css^headers^ | 2 + layout/style/test/media_queries_iframe.html | 15 + layout/style/test/media_queries_iframe2.html | 37 + layout/style/test/mochitest.toml | 783 + layout/style/test/moz.build | 152 + layout/style/test/mq_changes_child.html | 8 + layout/style/test/neverending_font_load.sjs | 5 + layout/style/test/neverending_stylesheet_load.sjs | 5 + layout/style/test/post-redirect-1.css | 1 + layout/style/test/post-redirect-2.css | 1 + layout/style/test/post-redirect-3.css | 1 + layout/style/test/property_database.js | 14128 +++++++++++++++++++ layout/style/test/redirect.sjs | 4 + layout/style/test/redundant_font_download.sjs | 63 + layout/style/test/slow_broken_sheet.sjs | 19 + layout/style/test/slow_load.sjs | 29 + layout/style/test/slow_ok_sheet.sjs | 21 + layout/style/test/sourcemap_css.html | 11 + layout/style/test/style_attribute_tests.js | 25 + layout/style/test/support/1x1-transparent.png | Bin 0 -> 89 bytes layout/style/test/support/blue-100x100.png | Bin 0 -> 40279 bytes .../style/test/support/external-variable-url.css | 3 + layout/style/test/test_acid3_test46.html | 140 + layout/style/test/test_addSheet.html | 44 + layout/style/test/test_additional_sheets.html | 310 + .../test/test_align_justify_computed_values.html | 484 + layout/style/test/test_all_shorthand.html | 157 + layout/style/test/test_animations.html | 2107 +++ layout/style/test/test_animations_async_tests.html | 24 + .../test/test_animations_dynamic_changes.html | 65 + .../test_animations_effect_timing_duration.html | 81 + .../test_animations_effect_timing_enddelay.html | 141 + .../test_animations_effect_timing_iterations.html | 68 + .../test_animations_event_handler_attribute.html | 204 + layout/style/test/test_animations_event_order.html | 710 + .../style/test/test_animations_iterationstart.html | 53 + layout/style/test/test_animations_omta.html | 2969 ++++ layout/style/test/test_animations_omta_scroll.html | 25 + .../test/test_animations_omta_scroll_rtl.html | 25 + layout/style/test/test_animations_omta_start.html | 187 + layout/style/test/test_animations_pausing.html | 79 + .../style/test/test_animations_playbackrate.html | 94 + layout/style/test/test_animations_reverse.html | 64 + .../test/test_animations_styles_on_event.html | 66 + .../test/test_animations_variable_changes.html | 58 + .../test_animations_with_disabled_properties.html | 33 + layout/style/test/test_any_dynamic.html | 49 + layout/style/test/test_area_url_cursor.html | 34 + layout/style/test/test_asyncopen.html | 54 + .../style/test/test_at_rule_parse_serialize.html | 43 + .../test/test_attribute_selector_eof_behavior.html | 18 + .../test/test_backdrop_filter_enabled_state.html | 21 + layout/style/test/test_background_blend_mode.html | 57 + ...border_device_pixel_rounding_initial_style.html | 20 + layout/style/test/test_box_size_keywords.html | 170 + layout/style/test/test_bug1055933.html | 41 + layout/style/test/test_bug1089417.html | 47 + layout/style/test/test_bug1112014.html | 89 + layout/style/test/test_bug1203766.html | 112 + layout/style/test/test_bug1232829.html | 37 + layout/style/test/test_bug1292447.html | 350 + layout/style/test/test_bug1330375.html | 59 + layout/style/test/test_bug1371488.html | 23 + layout/style/test/test_bug1375944.html | 34 + layout/style/test/test_bug1382568.html | 14 + layout/style/test/test_bug1394302.html | 32 + layout/style/test/test_bug1443344-1.html | 48 + layout/style/test/test_bug1443344-2.html | 48 + layout/style/test/test_bug1451199-1.html | 42 + layout/style/test/test_bug1451199-2.html | 43 + layout/style/test/test_bug1490890.html | 112 + layout/style/test/test_bug1505254.html | 152 + layout/style/test/test_bug160403.html | 73 + layout/style/test/test_bug1729861.html | 26 + layout/style/test/test_bug200089.html | 30 + layout/style/test/test_bug221428.html | 68 + layout/style/test/test_bug229915.html | 95 + layout/style/test/test_bug302186.html | 508 + layout/style/test/test_bug319381.html | 67 + layout/style/test/test_bug357614.html | 73 + layout/style/test/test_bug363146.html | 62 + layout/style/test/test_bug372770.html | 85 + layout/style/test/test_bug373293.html | 29 + layout/style/test/test_bug377947.html | 110 + layout/style/test/test_bug379440.html | 74 + layout/style/test/test_bug379741.html | 43 + layout/style/test/test_bug382027.html | 37 + layout/style/test/test_bug383075.html | 84 + layout/style/test/test_bug387615.html | 53 + layout/style/test/test_bug389464.html | 48 + layout/style/test/test_bug391034.html | 71 + layout/style/test/test_bug391221.html | 43 + layout/style/test/test_bug397427.html | 91 + layout/style/test/test_bug399349.html | 80 + layout/style/test/test_bug401046.html | 82 + layout/style/test/test_bug405818.html | 72 + layout/style/test/test_bug412901.html | 42 + layout/style/test/test_bug413958.html | 75 + layout/style/test/test_bug418986-2.html | 32 + layout/style/test/test_bug437915.html | 70 + layout/style/test/test_bug450191.html | 64 + layout/style/test/test_bug470769.html | 31 + layout/style/test/test_bug499655.html | 45 + layout/style/test/test_bug499655.xhtml | 48 + layout/style/test/test_bug517224.html | 45 + layout/style/test/test_bug524175.html | 28 + layout/style/test/test_bug525952.html | 46 + layout/style/test/test_bug534804.html | 89 + layout/style/test/test_bug573255.html | 32 + layout/style/test/test_bug580685.html | 41 + layout/style/test/test_bug621351.html | 35 + layout/style/test/test_bug635286.html | 52 + layout/style/test/test_bug645998.html | 29 + layout/style/test/test_bug652486.html | 192 + layout/style/test/test_bug657143.html | 120 + layout/style/test/test_bug667520.html | 50 + layout/style/test/test_bug716226.html | 52 + layout/style/test/test_bug732153.html | 22 + layout/style/test/test_bug732209.html | 95 + layout/style/test/test_bug73586.html | 189 + layout/style/test/test_bug74880.html | 119 + layout/style/test/test_bug765590.html | 21 + layout/style/test/test_bug771043.html | 69 + layout/style/test/test_bug795520.html | 39 + layout/style/test/test_bug798843_pref.html | 53 + layout/style/test/test_bug829816.html | 56 + layout/style/test/test_bug874919.html | 55 + ...st_bug887741_at-rules_in_declaration_lists.html | 75 + layout/style/test/test_bug892929.html | 74 + layout/style/test/test_bug98997.html | 144 + layout/style/test/test_cascade.html | 91 + layout/style/test/test_ch_ex_no_infloops.html | 60 + .../style/test/test_change_hint_optimizations.html | 57 + layout/style/test/test_clip-path_polygon.html | 40 + layout/style/test/test_color_rounding.html | 38 + .../test/test_compute_data_with_start_struct.html | 87 + layout/style/test/test_computed_style.html | 664 + .../test_computed_style_bfcache_display_none.html | 60 + .../style/test/test_computed_style_difference.html | 104 + .../test/test_computed_style_grid_with_pseudo.html | 91 + .../test_computed_style_in_created_document.html | 52 + .../test/test_computed_style_min_size_auto.html | 129 + .../style/test/test_computed_style_no_flush.html | 63 + .../style/test/test_computed_style_no_pseudo.html | 53 + layout/style/test/test_computed_style_prefs.html | 94 + layout/style/test/test_condition_text.html | 74 + ...e_stylesheets_chrome_only_rules_in_content.html | 17 + .../test/test_counter_descriptor_storage.html | 268 + layout/style/test/test_counter_style.html | 121 + .../style/test/test_crash_with_content_policy.html | 75 + layout/style/test/test_css_cross_domain.html | 158 + .../style/test/test_css_cross_domain_no_orb.html | 147 + layout/style/test/test_css_eof_handling.html | 268 + layout/style/test/test_css_escape_api.html | 94 + .../test_css_function_mismatched_parenthesis.html | 63 + .../test/test_css_loader_crossorigin_data_url.html | 17 + .../style/test/test_css_parse_error_smoketest.html | 160 + layout/style/test/test_css_supports.html | 134 + layout/style/test/test_css_supports_variables.html | 247 + layout/style/test/test_cue_restrictions.html | 34 + .../test/test_custom_content_inheritance.html | 24 + layout/style/test/test_default_bidi_css.html | 80 + layout/style/test/test_default_computed_style.html | 58 + layout/style/test/test_descriptor_storage.html | 118 + .../style/test/test_descriptor_syntax_errors.html | 53 + layout/style/test/test_display_mode.html | 70 + .../style/test/test_dont_use_document_colors.html | 201 + .../style/test/test_dont_use_document_fonts.html | 116 + .../test/test_dynamic_change_causing_reflow.html | 1014 ++ layout/style/test/test_exposed_prop_accessors.html | 41 + layout/style/test/test_extra_inherit_initial.html | 109 + .../style/test/test_first_letter_restrictions.html | 56 + .../style/test/test_first_line_restrictions.html | 56 + .../test/test_flexbox_child_display_values.xhtml | 178 + .../test/test_flexbox_flex_grow_and_shrink.html | 154 + layout/style/test/test_flexbox_flex_shorthand.html | 280 + layout/style/test/test_flexbox_focus_order.html | 83 + layout/style/test/test_flexbox_layout.html | 184 + layout/style/test/test_flexbox_order.html | 194 + layout/style/test/test_flexbox_order_abspos.html | 217 + layout/style/test/test_flexbox_order_table.html | 198 + layout/style/test/test_flexbox_reflow_counts.html | 199 + layout/style/test/test_flushing_frame.html | 40 + layout/style/test/test_font_face_cascade.html | 35 + layout/style/test/test_font_face_parser.html | 386 + layout/style/test/test_font_family_parsing.html | 272 + .../style/test/test_font_family_serialization.html | 51 + layout/style/test/test_font_loading_api.html | 1895 +++ .../test/test_garbage_at_end_of_declarations.html | 156 + layout/style/test/test_grid_computed_values.html | 113 + .../style/test/test_grid_container_shorthands.html | 271 + layout/style/test/test_grid_item_shorthands.html | 153 + .../test/test_grid_shorthand_serialization.html | 221 + layout/style/test/test_group_insertRule.html | 243 + layout/style/test/test_hover_on_part.html | 52 + layout/style/test/test_hover_quirk.html | 118 + .../test/test_html_attribute_computed_values.html | 84 + layout/style/test/test_ident_escaping.html | 56 + layout/style/test/test_img_src_causing_reflow.html | 36 + layout/style/test/test_import_preload.html | 22 + layout/style/test/test_inherit_computation.html | 176 + layout/style/test/test_inherit_storage.html | 120 + layout/style/test/test_initial_computation.html | 159 + layout/style/test/test_initial_storage.html | 134 + layout/style/test/test_invalidation_basic.html | 45 + layout/style/test/test_keyframes_rules.html | 134 + .../style/test/test_keyframes_vendor_prefix.html | 167 + .../test/test_load_events_on_stylesheets.html | 176 + layout/style/test/test_logical_properties.html | 422 + layout/style/test/test_marker_restrictions.html | 35 + layout/style/test/test_mask_image_CORS.html | 63 + layout/style/test/test_media_queries.html | 867 ++ layout/style/test/test_media_queries_dynamic.html | 207 + layout/style/test/test_media_query_list.html | 373 + .../style/test/test_media_query_serialization.html | 53 + layout/style/test/test_moz_device_pixel_ratio.html | 73 + layout/style/test/test_moz_prefixed_cursor.html | 14 + .../test/test_mq_any_hover_and_any_pointer.html | 97 + layout/style/test/test_mq_changes_in_iframe.html | 54 + layout/style/test/test_mq_hover_and_pointer.html | 127 + .../test/test_mq_prefers_contrast_dynamic.html | 82 + .../test_mq_prefers_reduced_motion_dynamic.html | 86 + .../style/test/test_mql_event_listener_leaks.html | 43 + layout/style/test/test_namespace_rule.html | 461 + .../test/test_non_content_accessible_env_vars.html | 39 + .../test_non_content_accessible_properties.html | 85 + .../test/test_non_content_accessible_pseudos.html | 69 + .../test/test_non_content_accessible_values.html | 172 + .../style/test/test_non_matching_sheet_media.html | 30 + layout/style/test/test_of_type_selectors.xhtml | 97 + .../style/test/test_overscroll_behavior_pref.html | 24 + layout/style/test/test_page_parser.html | 93 + layout/style/test/test_parse_eof.html | 69 + layout/style/test/test_parse_ident.html | 56 + layout/style/test/test_parse_rule.html | 261 + layout/style/test/test_parse_url.html | 195 + .../test/test_parser_diagnostics_unprintables.html | 220 + layout/style/test/test_pixel_lengths.html | 61 + .../style/test/test_placeholder_restrictions.html | 57 + layout/style/test/test_pointer-events.html | 114 + layout/style/test/test_position_float_display.html | 111 + layout/style/test/test_position_sticky.html | 89 + .../test/test_prefers_contrast_color_pairs.html | 49 + layout/style/test/test_priority_preservation.html | 141 + layout/style/test/test_property_database.html | 173 + layout/style/test/test_property_syntax_errors.html | 155 + layout/style/test/test_pseudo_display_fixup.html | 29 + layout/style/test/test_pseudoelement_parsing.html | 43 + layout/style/test/test_pseudoelement_state.html | 185 + layout/style/test/test_query_container_for.html | 62 + .../style/test/test_redundant_font_download.html | 131 + layout/style/test/test_reframe_cb.html | 56 + layout/style/test/test_reframe_image_loading.html | 29 + layout/style/test/test_reframe_input.html | 48 + layout/style/test/test_reframe_pseudo_element.html | 46 + layout/style/test/test_rem_unit.html | 80 + layout/style/test/test_restyle_table_wrapper.html | 33 + .../test/test_restyles_in_smil_animation.html | 137 + layout/style/test/test_revert.html | 103 + layout/style/test/test_root_node_display.html | 74 + layout/style/test/test_rule_insertion.html | 240 + layout/style/test/test_rules_out_of_sheets.html | 115 + layout/style/test/test_selectors.html | 1348 ++ layout/style/test/test_setPropertyWithNull.html | 47 + layout/style/test/test_shape_outside_CORS.html | 59 + layout/style/test/test_shared_sheet_caching.html | 35 + layout/style/test/test_sheet_privilege.html | 33 + .../test/test_shorthand_property_getters.html | 248 + .../test/test_specified_value_serialization.html | 281 + layout/style/test/test_style_attr_listener.html | 52 + layout/style/test/test_style_attribute_quirks.html | 18 + .../style/test/test_style_attribute_standards.html | 19 + .../test/test_style_struct_copy_constructors.html | 93 + layout/style/test/test_stylesheet_additions.html | 68 + .../test/test_stylesheet_clone_font_face.html | 26 + layout/style/test/test_supports_rules.html | 88 + .../style/test/test_system_font_serialization.html | 84 + .../test/test_text_decoration_shorthands.html | 136 + layout/style/test/test_transitions.html | 787 ++ .../style/test/test_transitions_and_reframes.html | 298 + .../style/test/test_transitions_and_restyles.html | 48 + layout/style/test/test_transitions_and_zoom.html | 49 + layout/style/test/test_transitions_at_start.html | 39 + layout/style/test/test_transitions_bug537151.html | 51 + .../test/test_transitions_cancel_near_end.html | 82 + ...st_transitions_computed_value_combinations.html | 170 + .../test/test_transitions_computed_values.html | 113 + .../test/test_transitions_dynamic_changes.html | 106 + layout/style/test/test_transitions_events.html | 294 + .../style/test/test_transitions_per_property.html | 3245 +++++ ...test_transitions_replacement_on_busy_frame.html | 100 + ..._transitions_replacement_with_setKeyframes.html | 88 + .../test/test_transitions_step_functions.html | 131 + layout/style/test/test_unclosed_parentheses.html | 262 + layout/style/test/test_unicode_range_loading.html | 366 + layout/style/test/test_units_angle.html | 52 + layout/style/test/test_units_frequency.html | 56 + layout/style/test/test_units_length.html | 60 + layout/style/test/test_units_time.html | 47 + layout/style/test/test_use_counters.html | 159 + layout/style/test/test_user_sheet_shadow_dom.html | 48 + layout/style/test/test_value_cloning.html | 181 + layout/style/test/test_value_computation.html | 236 + layout/style/test/test_value_storage.html | 365 + .../test/test_variable_serialization_computed.html | 79 + .../test_variable_serialization_specified.html | 116 + layout/style/test/test_variables.html | 129 + layout/style/test/test_variables_loop.html | 31 + layout/style/test/test_variables_order.html | 53 + layout/style/test/test_video_object_fit.html | 53 + .../test_viewport_scrollbar_causing_reflow.html | 130 + layout/style/test/test_viewport_units.html | 66 + layout/style/test/test_visited_image_loading.html | 68 + .../test/test_visited_image_loading_empty.html | 68 + layout/style/test/test_visited_lying.html | 97 + layout/style/test/test_visited_pref.html | 112 + layout/style/test/test_visited_reftests.html | 210 + .../style/test/test_webkit_device_pixel_ratio.html | 73 + layout/style/test/test_webkit_flex_display.html | 46 + layout/style/test/unstyled-frame.css | 0 layout/style/test/unstyled-frame.xml | 4 + layout/style/test/unstyled.css | 2 + layout/style/test/unstyled.xml | 3 + layout/style/test/viewport_units_iframe.html | 6 + layout/style/test/visited-lying-inner.html | 8 + layout/style/test/visited-pref-iframe.html | 7 + layout/style/test/visited_image_loading.sjs | 83 + layout/style/test/visited_image_loading_frame.html | 15 + .../test/visited_image_loading_frame_empty.html | 15 + layout/style/tools/cleanup_computed_getters.py | 85 + 1000 files changed, 132168 insertions(+) create mode 100644 layout/style/AnimatedPropertyID.h create mode 100644 layout/style/AnimatedPropertyIDSet.h create mode 100644 layout/style/AnimationCollection.cpp create mode 100644 layout/style/AnimationCollection.h create mode 100644 layout/style/AnimationCommon.h create mode 100644 layout/style/AttributeStyles.cpp create mode 100644 layout/style/AttributeStyles.h create mode 100644 layout/style/BuiltinCounterStyleList.h create mode 100644 layout/style/CSS.cpp create mode 100644 layout/style/CSS.h create mode 100644 layout/style/CSSContainerRule.cpp create mode 100644 layout/style/CSSContainerRule.h create mode 100644 layout/style/CSSCounterStyleRule.cpp create mode 100644 layout/style/CSSCounterStyleRule.h create mode 100644 layout/style/CSSEnabledState.h create mode 100644 layout/style/CSSFontFaceRule.cpp create mode 100644 layout/style/CSSFontFaceRule.h create mode 100644 layout/style/CSSFontFeatureValuesRule.cpp create mode 100644 layout/style/CSSFontFeatureValuesRule.h create mode 100644 layout/style/CSSFontPaletteValuesRule.cpp create mode 100644 layout/style/CSSFontPaletteValuesRule.h create mode 100644 layout/style/CSSImportRule.cpp create mode 100644 layout/style/CSSImportRule.h create mode 100644 layout/style/CSSKeyframeRule.cpp create mode 100644 layout/style/CSSKeyframeRule.h create mode 100644 layout/style/CSSKeyframesRule.cpp create mode 100644 layout/style/CSSKeyframesRule.h create mode 100644 layout/style/CSSLayerBlockRule.cpp create mode 100644 layout/style/CSSLayerBlockRule.h create mode 100644 layout/style/CSSLayerStatementRule.cpp create mode 100644 layout/style/CSSLayerStatementRule.h create mode 100644 layout/style/CSSMediaRule.cpp create mode 100644 layout/style/CSSMediaRule.h create mode 100644 layout/style/CSSMozDocumentRule.cpp create mode 100644 layout/style/CSSMozDocumentRule.h create mode 100644 layout/style/CSSNamespaceRule.cpp create mode 100644 layout/style/CSSNamespaceRule.h create mode 100644 layout/style/CSSPageRule.cpp create mode 100644 layout/style/CSSPageRule.h create mode 100644 layout/style/CSSPropFlags.h create mode 100644 layout/style/CSSPropertyRule.cpp create mode 100644 layout/style/CSSPropertyRule.h create mode 100644 layout/style/CSSRuleList.cpp create mode 100644 layout/style/CSSRuleList.h create mode 100644 layout/style/CSSStyleRule.cpp create mode 100644 layout/style/CSSStyleRule.h create mode 100644 layout/style/CSSSupportsRule.cpp create mode 100644 layout/style/CSSSupportsRule.h create mode 100644 layout/style/CSSValue.h create mode 100644 layout/style/CachedInheritingStyles.cpp create mode 100644 layout/style/CachedInheritingStyles.h create mode 100644 layout/style/ComputedStyle.cpp create mode 100644 layout/style/ComputedStyle.h create mode 100644 layout/style/ComputedStyleInlines.h create mode 100644 layout/style/CounterStyleManager.cpp create mode 100644 layout/style/CounterStyleManager.h create mode 100644 layout/style/DeclarationBlock.cpp create mode 100644 layout/style/DeclarationBlock.h create mode 100644 layout/style/DocumentMatchingFunction.h create mode 100644 layout/style/DocumentStyleRootIterator.cpp create mode 100644 layout/style/DocumentStyleRootIterator.h create mode 100644 layout/style/ErrorReporter.cpp create mode 100644 layout/style/ErrorReporter.h create mode 100644 layout/style/FontFace.cpp create mode 100644 layout/style/FontFace.h create mode 100644 layout/style/FontFaceImpl.cpp create mode 100644 layout/style/FontFaceImpl.h create mode 100644 layout/style/FontFaceSet.cpp create mode 100644 layout/style/FontFaceSet.h create mode 100644 layout/style/FontFaceSetDocumentImpl.cpp create mode 100644 layout/style/FontFaceSetDocumentImpl.h create mode 100644 layout/style/FontFaceSetImpl.cpp create mode 100644 layout/style/FontFaceSetImpl.h create mode 100644 layout/style/FontFaceSetIterator.cpp create mode 100644 layout/style/FontFaceSetIterator.h create mode 100644 layout/style/FontFaceSetWorkerImpl.cpp create mode 100644 layout/style/FontFaceSetWorkerImpl.h create mode 100644 layout/style/FontLoaderUtils.cpp create mode 100644 layout/style/FontLoaderUtils.h create mode 100644 layout/style/FontPreloader.cpp create mode 100644 layout/style/FontPreloader.h create mode 100644 layout/style/GeckoBindings.cpp create mode 100644 layout/style/GeckoBindings.h create mode 100644 layout/style/GenerateCSSPropertyID.py create mode 100644 layout/style/GenerateCSSPropsGenerated.py create mode 100644 layout/style/GenerateCompositorAnimatableProperties.py create mode 100644 layout/style/GenerateComputedDOMStyleGenerated.py create mode 100644 layout/style/GenerateCountedUnknownProperties.py create mode 100644 layout/style/GenerateServoCSSPropList.py create mode 100644 layout/style/GlobalStyleSheetCache.cpp create mode 100644 layout/style/GlobalStyleSheetCache.h create mode 100644 layout/style/GroupRule.cpp create mode 100644 layout/style/GroupRule.h create mode 100644 layout/style/ImageDocument.css create mode 100644 layout/style/ImageLoader.cpp create mode 100644 layout/style/ImageLoader.h create mode 100644 layout/style/ImportScanner.cpp create mode 100644 layout/style/ImportScanner.h create mode 100644 layout/style/LayerAnimationInfo.cpp create mode 100644 layout/style/LayerAnimationInfo.h create mode 100644 layout/style/Loader.cpp create mode 100644 layout/style/Loader.h create mode 100644 layout/style/MappedDeclarationsBuilder.cpp create mode 100644 layout/style/MappedDeclarationsBuilder.h create mode 100644 layout/style/MediaFeatureChange.h create mode 100644 layout/style/MediaList.cpp create mode 100644 layout/style/MediaList.h create mode 100644 layout/style/MediaQueryList.cpp create mode 100644 layout/style/MediaQueryList.h create mode 100644 layout/style/PaintWorkletGlobalScope.cpp create mode 100644 layout/style/PaintWorkletGlobalScope.h create mode 100644 layout/style/PaintWorkletImpl.cpp create mode 100644 layout/style/PaintWorkletImpl.h create mode 100644 layout/style/PostTraversalTask.cpp create mode 100644 layout/style/PostTraversalTask.h create mode 100644 layout/style/PreferenceSheet.cpp create mode 100644 layout/style/PreferenceSheet.h create mode 100644 layout/style/PreloadedStyleSheet.cpp create mode 100644 layout/style/PreloadedStyleSheet.h create mode 100644 layout/style/PseudoStyleType.cpp create mode 100644 layout/style/PseudoStyleType.h create mode 100644 layout/style/Rule.cpp create mode 100644 layout/style/Rule.h create mode 100644 layout/style/RustCell.h create mode 100644 layout/style/ServoBindingTypes.h create mode 100644 layout/style/ServoBindings.h create mode 100644 layout/style/ServoBindings.toml create mode 100644 layout/style/ServoBoxedTypeList.h create mode 100644 layout/style/ServoCSSParser.cpp create mode 100644 layout/style/ServoCSSParser.h create mode 100644 layout/style/ServoCSSPropList.mako.py create mode 100644 layout/style/ServoCSSRuleList.cpp create mode 100644 layout/style/ServoCSSRuleList.h create mode 100644 layout/style/ServoComputedData.h create mode 100644 layout/style/ServoElementSnapshot.cpp create mode 100644 layout/style/ServoElementSnapshot.h create mode 100644 layout/style/ServoElementSnapshotTable.h create mode 100644 layout/style/ServoLockedArcTypeList.h create mode 100644 layout/style/ServoStyleConstsForwards.h create mode 100644 layout/style/ServoStyleConstsInlines.h create mode 100644 layout/style/ServoStyleSet.cpp create mode 100644 layout/style/ServoStyleSet.h create mode 100644 layout/style/ServoStyleSetInlines.h create mode 100644 layout/style/ServoTraversalStatistics.h create mode 100644 layout/style/ServoTypes.h create mode 100644 layout/style/ServoUtils.h create mode 100644 layout/style/ShadowParts.cpp create mode 100644 layout/style/ShadowParts.h create mode 100644 layout/style/SharedStyleSheetCache.cpp create mode 100644 layout/style/SharedStyleSheetCache.h create mode 100644 layout/style/SharedSubResourceCache.h create mode 100644 layout/style/SheetLoadData.h create mode 100644 layout/style/SheetParsingMode.h create mode 100644 layout/style/StreamLoader.cpp create mode 100644 layout/style/StreamLoader.h create mode 100644 layout/style/StyleAnimationValue.cpp create mode 100644 layout/style/StyleAnimationValue.h create mode 100644 layout/style/StyleColor.cpp create mode 100644 layout/style/StyleColorInlines.h create mode 100644 layout/style/StylePreloadKind.h create mode 100644 layout/style/StyleSheet.cpp create mode 100644 layout/style/StyleSheet.h create mode 100644 layout/style/StyleSheetInfo.h create mode 100644 layout/style/StyleSheetInlines.h create mode 100644 layout/style/TimelineCollection.cpp create mode 100644 layout/style/TimelineCollection.h create mode 100644 layout/style/TimelineManager.cpp create mode 100644 layout/style/TimelineManager.h create mode 100644 layout/style/TopLevelImageDocument.css create mode 100644 layout/style/TopLevelVideoDocument.css create mode 100644 layout/style/URLExtraData.cpp create mode 100644 layout/style/URLExtraData.h create mode 100644 layout/style/UserAgentStyleSheetID.h create mode 100644 layout/style/UserAgentStyleSheetList.h create mode 100644 layout/style/contenteditable.css create mode 100644 layout/style/crashtests/1017798-1.css create mode 100644 layout/style/crashtests/1017798-1.html create mode 100644 layout/style/crashtests/1028514-1.html create mode 100644 layout/style/crashtests/105619-1.html create mode 100644 layout/style/crashtests/1066089-1.html create mode 100644 layout/style/crashtests/1074651-1.html create mode 100644 layout/style/crashtests/1089463-1.html create mode 100644 layout/style/crashtests/1135534.html create mode 100644 layout/style/crashtests/1136010-1.html create mode 100644 layout/style/crashtests/1146101-1.html create mode 100644 layout/style/crashtests/1153693-1.html create mode 100644 layout/style/crashtests/1156969.svg create mode 100644 layout/style/crashtests/1161320-1.html create mode 100644 layout/style/crashtests/1161320-2.html create mode 100644 layout/style/crashtests/1161366-1.html create mode 100644 layout/style/crashtests/1163446-1.html create mode 100644 layout/style/crashtests/1164813-1.html create mode 100644 layout/style/crashtests/1167782-1.html create mode 100644 layout/style/crashtests/1186768-1.xhtml create mode 100644 layout/style/crashtests/1200568-1.html create mode 100644 layout/style/crashtests/1206105-1.html create mode 100644 layout/style/crashtests/1223688-1.html create mode 100644 layout/style/crashtests/1223694-1.html create mode 100644 layout/style/crashtests/1226400-1.html create mode 100644 layout/style/crashtests/1227498.html create mode 100644 layout/style/crashtests/1227501-1.html create mode 100644 layout/style/crashtests/1228789-1.html create mode 100644 layout/style/crashtests/1230408-1.html create mode 100644 layout/style/crashtests/1233135-1.html create mode 100644 layout/style/crashtests/1233135-2.html create mode 100644 layout/style/crashtests/1236398.xhtml create mode 100644 layout/style/crashtests/1238660-1.html create mode 100644 layout/style/crashtests/1245260-1.html create mode 100644 layout/style/crashtests/1247865-1.html create mode 100644 layout/style/crashtests/1250791.html create mode 100644 layout/style/crashtests/1264396-1.html create mode 100644 layout/style/crashtests/1264949.html create mode 100644 layout/style/crashtests/1265611-1.html create mode 100644 layout/style/crashtests/1270795.html create mode 100644 layout/style/crashtests/1275026.html create mode 100644 layout/style/crashtests/1277908-1.html create mode 100644 layout/style/crashtests/1277908-2.html create mode 100644 layout/style/crashtests/1278463-1.html create mode 100644 layout/style/crashtests/1279819-1.html create mode 100644 layout/style/crashtests/1282076-1.html create mode 100644 layout/style/crashtests/1282076-2.html create mode 100644 layout/style/crashtests/1290994-1.html create mode 100644 layout/style/crashtests/1290994-2.html create mode 100644 layout/style/crashtests/1290994-3.html create mode 100644 layout/style/crashtests/1290994-4.html create mode 100644 layout/style/crashtests/1314531.html create mode 100644 layout/style/crashtests/1315889-1.html create mode 100644 layout/style/crashtests/1315894-1.html create mode 100644 layout/style/crashtests/1319072-1.html create mode 100644 layout/style/crashtests/1320423-1.html create mode 100644 layout/style/crashtests/1321357-1.html create mode 100644 layout/style/crashtests/1328535-1.html create mode 100644 layout/style/crashtests/1331272.html create mode 100644 layout/style/crashtests/1332550.html create mode 100644 layout/style/crashtests/1333001-1.css create mode 100644 layout/style/crashtests/1333001-1.html create mode 100644 layout/style/crashtests/1340248.html create mode 100644 layout/style/crashtests/1340344.html create mode 100644 layout/style/crashtests/1342316-1.html create mode 100644 layout/style/crashtests/1344210.html create mode 100644 layout/style/crashtests/1353312.html create mode 100644 layout/style/crashtests/1356601-1.html create mode 100644 layout/style/crashtests/1364139-1.html create mode 100644 layout/style/crashtests/1371450-1.html create mode 100644 layout/style/crashtests/1374175-1.html create mode 100644 layout/style/crashtests/1375812-1.html create mode 100644 layout/style/crashtests/1377053-1.html create mode 100644 layout/style/crashtests/1377256-1-helper.html create mode 100644 layout/style/crashtests/1377256-1.html create mode 100644 layout/style/crashtests/1378064-1.html create mode 100644 layout/style/crashtests/1378814.html create mode 100644 layout/style/crashtests/1380800.html create mode 100644 layout/style/crashtests/1381420-1.html create mode 100644 layout/style/crashtests/1381682.html create mode 100644 layout/style/crashtests/1382672.html create mode 100644 layout/style/crashtests/1382710.html create mode 100644 layout/style/crashtests/1383001-2.html create mode 100644 layout/style/crashtests/1383001.html create mode 100644 layout/style/crashtests/1383319.html create mode 100644 layout/style/crashtests/1383493-1.html create mode 100644 layout/style/crashtests/1383589-1.html create mode 100644 layout/style/crashtests/1383975.html create mode 100644 layout/style/crashtests/1383981-2.html create mode 100644 layout/style/crashtests/1383981-3.html create mode 100644 layout/style/crashtests/1383981.html create mode 100644 layout/style/crashtests/1384232.html create mode 100644 layout/style/crashtests/1384824-1.html create mode 100644 layout/style/crashtests/1384824-2.html create mode 100644 layout/style/crashtests/1386773.html create mode 100644 layout/style/crashtests/1387481-1-iframe.html create mode 100644 layout/style/crashtests/1387481-1.html create mode 100644 layout/style/crashtests/1387499.html create mode 100644 layout/style/crashtests/1388234.html create mode 100644 layout/style/crashtests/1389645.html create mode 100644 layout/style/crashtests/1390726.html create mode 100644 layout/style/crashtests/1391577.html create mode 100644 layout/style/crashtests/1393189.html create mode 100644 layout/style/crashtests/1393580.html create mode 100644 layout/style/crashtests/1393791.html create mode 100644 layout/style/crashtests/1395719.html create mode 100644 layout/style/crashtests/1395725.html create mode 100644 layout/style/crashtests/1396041.html create mode 100644 layout/style/crashtests/1397091.html create mode 100644 layout/style/crashtests/1397363-1.html create mode 100644 layout/style/crashtests/1397439-1.html create mode 100644 layout/style/crashtests/1398479.html create mode 100644 layout/style/crashtests/1398581.html create mode 100644 layout/style/crashtests/1399006.html create mode 100644 layout/style/crashtests/1399546.html create mode 100644 layout/style/crashtests/1400035.html create mode 100644 layout/style/crashtests/1400325.html create mode 100644 layout/style/crashtests/1400926.html create mode 100644 layout/style/crashtests/1400936-1.html create mode 100644 layout/style/crashtests/1400936-2.html create mode 100644 layout/style/crashtests/1401256.html create mode 100644 layout/style/crashtests/1401706.html create mode 100644 layout/style/crashtests/1401801.html create mode 100644 layout/style/crashtests/1401825.html create mode 100644 layout/style/crashtests/1402218-1.html create mode 100644 layout/style/crashtests/1402366.html create mode 100644 layout/style/crashtests/1402419.html create mode 100644 layout/style/crashtests/1402472.html create mode 100644 layout/style/crashtests/1403028.html create mode 100644 layout/style/crashtests/1403433.html create mode 100644 layout/style/crashtests/1403465.html create mode 100644 layout/style/crashtests/1403592.html create mode 100644 layout/style/crashtests/1403615.html create mode 100644 layout/style/crashtests/1403712.html create mode 100644 layout/style/crashtests/1404057.html create mode 100644 layout/style/crashtests/1404180-1.html create mode 100644 layout/style/crashtests/1404316.html create mode 100644 layout/style/crashtests/1404324-1.html create mode 100644 layout/style/crashtests/1404324-2.html create mode 100644 layout/style/crashtests/1404324-3.html create mode 100644 layout/style/crashtests/1405880.html create mode 100644 layout/style/crashtests/1406222-1.html create mode 100644 layout/style/crashtests/1406222-2.html create mode 100644 layout/style/crashtests/1409183.html create mode 100644 layout/style/crashtests/1409502.html create mode 100644 layout/style/crashtests/1409931.html create mode 100644 layout/style/crashtests/1410226-1.html create mode 100644 layout/style/crashtests/1410226-2.html create mode 100644 layout/style/crashtests/1411008.html create mode 100644 layout/style/crashtests/1411143.html create mode 100644 layout/style/crashtests/1411478.html create mode 100644 layout/style/crashtests/1413288.html create mode 100644 layout/style/crashtests/1413361.html create mode 100644 layout/style/crashtests/1413670.html create mode 100644 layout/style/crashtests/1415353.html create mode 100644 layout/style/crashtests/1418059.html create mode 100644 layout/style/crashtests/1418867.html create mode 100644 layout/style/crashtests/1419554.html create mode 100644 layout/style/crashtests/1426312.html create mode 100644 layout/style/crashtests/1439793.html create mode 100644 layout/style/crashtests/1445682.html create mode 100644 layout/style/crashtests/1449243.html create mode 100644 layout/style/crashtests/1450691.html create mode 100644 layout/style/crashtests/1453206.html create mode 100644 layout/style/crashtests/1454140.html create mode 100644 layout/style/crashtests/1455108.html create mode 100644 layout/style/crashtests/1457288.html create mode 100644 layout/style/crashtests/1457985.html create mode 100644 layout/style/crashtests/1468640.html create mode 100644 layout/style/crashtests/1469076.html create mode 100644 layout/style/crashtests/1475003.html create mode 100644 layout/style/crashtests/1479681.html create mode 100644 layout/style/crashtests/1488817.html create mode 100644 layout/style/crashtests/1490012.html create mode 100644 layout/style/crashtests/1502893.html create mode 100644 layout/style/crashtests/1507674.html create mode 100644 layout/style/crashtests/1509989.html create mode 100644 layout/style/crashtests/1514086.html create mode 100644 layout/style/crashtests/1533783.html create mode 100644 layout/style/crashtests/1533891.html create mode 100644 layout/style/crashtests/1533968.html create mode 100644 layout/style/crashtests/1545177.html create mode 100644 layout/style/crashtests/1546255.html create mode 100644 layout/style/crashtests/1552911.html create mode 100644 layout/style/crashtests/1562361.html create mode 100644 layout/style/crashtests/1566684.html create mode 100644 layout/style/crashtests/1579788.html create mode 100644 layout/style/crashtests/1580307.html create mode 100644 layout/style/crashtests/1581579.html create mode 100644 layout/style/crashtests/1586444.html create mode 100644 layout/style/crashtests/1593766.html create mode 100644 layout/style/crashtests/1594949.html create mode 100644 layout/style/crashtests/1594960.html create mode 100644 layout/style/crashtests/1599286.html create mode 100644 layout/style/crashtests/1609786.html create mode 100644 layout/style/crashtests/1616407.html create mode 100644 layout/style/crashtests/1616433.html create mode 100644 layout/style/crashtests/1639533.html create mode 100644 layout/style/crashtests/1640040.html create mode 100644 layout/style/crashtests/1806189-1.html create mode 100644 layout/style/crashtests/1821416.html create mode 100644 layout/style/crashtests/1872309.html create mode 100644 layout/style/crashtests/187671-1.html create mode 100644 layout/style/crashtests/192408-1.html create mode 100644 layout/style/crashtests/285727-1.html create mode 100644 layout/style/crashtests/286707-1.html create mode 100644 layout/style/crashtests/317561-1.html create mode 100644 layout/style/crashtests/330998-1.html create mode 100644 layout/style/crashtests/363950.html create mode 100644 layout/style/crashtests/368175-1.html create mode 100644 layout/style/crashtests/368740.html create mode 100644 layout/style/crashtests/379788-1.html create mode 100644 layout/style/crashtests/383979-1.xhtml create mode 100644 layout/style/crashtests/383979-2.html create mode 100644 layout/style/crashtests/386939-1.html create mode 100644 layout/style/crashtests/391034-1.xhtml create mode 100644 layout/style/crashtests/397022-1.html create mode 100644 layout/style/crashtests/399289-1.svg create mode 100644 layout/style/crashtests/404470-1.html create mode 100644 layout/style/crashtests/411603-1.html create mode 100644 layout/style/crashtests/412588-1.html create mode 100644 layout/style/crashtests/413274-1.xhtml create mode 100644 layout/style/crashtests/416461-1.xhtml create mode 100644 layout/style/crashtests/418007-1.xhtml create mode 100644 layout/style/crashtests/431705-1.xhtml create mode 100644 layout/style/crashtests/432561-1.html create mode 100644 layout/style/crashtests/437170-1.html create mode 100644 layout/style/crashtests/437532-1.html create mode 100644 layout/style/crashtests/439184-1.html create mode 100644 layout/style/crashtests/444237-1.html create mode 100644 layout/style/crashtests/444848-1.html create mode 100644 layout/style/crashtests/447776-1.html create mode 100644 layout/style/crashtests/447783-1.html create mode 100644 layout/style/crashtests/448161-1.html create mode 100644 layout/style/crashtests/448161-2.html create mode 100644 layout/style/crashtests/452150-1.xhtml create mode 100644 layout/style/crashtests/456196.html create mode 100644 layout/style/crashtests/460209-1.html create mode 100644 layout/style/crashtests/460217-1.html create mode 100644 layout/style/crashtests/460323-1.html create mode 100644 layout/style/crashtests/466845-1.html create mode 100644 layout/style/crashtests/469432-1.xhtml create mode 100644 layout/style/crashtests/472195-1.html create mode 100644 layout/style/crashtests/472237-1.html create mode 100644 layout/style/crashtests/473720-1.html create mode 100644 layout/style/crashtests/473892-1.html create mode 100644 layout/style/crashtests/473914-1.html create mode 100644 layout/style/crashtests/474377-1.xhtml create mode 100644 layout/style/crashtests/478321-1.xhtml create mode 100644 layout/style/crashtests/481557.html create mode 100644 layout/style/crashtests/495269-1.html create mode 100644 layout/style/crashtests/495269-2.html create mode 100644 layout/style/crashtests/498036-1.html create mode 100644 layout/style/crashtests/509155-1.html create mode 100644 layout/style/crashtests/509156-1.html create mode 100644 layout/style/crashtests/509569-1.html create mode 100644 layout/style/crashtests/512851-1.xhtml create mode 100644 layout/style/crashtests/524252-1.html create mode 100644 layout/style/crashtests/536789-1.html create mode 100644 layout/style/crashtests/539613-1.xhtml create mode 100644 layout/style/crashtests/558943-1.xhtml create mode 100644 layout/style/crashtests/559491.html create mode 100644 layout/style/crashtests/565248-1.html create mode 100644 layout/style/crashtests/571105-1.xhtml create mode 100644 layout/style/crashtests/573127-1.html create mode 100644 layout/style/crashtests/575464-1.html create mode 100644 layout/style/crashtests/580685.html create mode 100644 layout/style/crashtests/585185-1.html create mode 100644 layout/style/crashtests/588627-1.html create mode 100644 layout/style/crashtests/592698-1.html create mode 100644 layout/style/crashtests/601437-1.html create mode 100644 layout/style/crashtests/601439-1.html create mode 100644 layout/style/crashtests/605689-1.html create mode 100644 layout/style/crashtests/611922-1.html create mode 100644 layout/style/crashtests/612213.html create mode 100644 layout/style/crashtests/621596-1.html create mode 100644 layout/style/crashtests/622314-1.xhtml create mode 100644 layout/style/crashtests/635153.html create mode 100644 layout/style/crashtests/637242.xhtml create mode 100644 layout/style/crashtests/645142.html create mode 100644 layout/style/crashtests/652976-1.svg create mode 100644 layout/style/crashtests/653675.html create mode 100644 layout/style/crashtests/665209-1.html create mode 100644 layout/style/crashtests/671799-1.html create mode 100644 layout/style/crashtests/671799-2.html create mode 100644 layout/style/crashtests/690990-1.html create mode 100644 layout/style/crashtests/694775.html create mode 100644 layout/style/crashtests/696188-1.html create mode 100644 layout/style/crashtests/696869-1.html create mode 100644 layout/style/crashtests/700116.html create mode 100644 layout/style/crashtests/729126-1.html create mode 100644 layout/style/crashtests/729126-2.html create mode 100644 layout/style/crashtests/786108-1.html create mode 100644 layout/style/crashtests/786108-2.html create mode 100644 layout/style/crashtests/788836.html create mode 100644 layout/style/crashtests/806310-1.html create mode 100644 layout/style/crashtests/809762.html create mode 100644 layout/style/crashtests/812824.html create mode 100644 layout/style/crashtests/822766-1.html create mode 100644 layout/style/crashtests/822877.html create mode 100644 layout/style/crashtests/827213-1.html create mode 100644 layout/style/crashtests/827220.html create mode 100644 layout/style/crashtests/827591-1.html create mode 100644 layout/style/crashtests/829817.html create mode 100644 layout/style/crashtests/842134.html create mode 100644 layout/style/crashtests/861489-1.html create mode 100644 layout/style/crashtests/862113.html create mode 100644 layout/style/crashtests/867487.html create mode 100644 layout/style/crashtests/873222.html create mode 100644 layout/style/crashtests/873260-1.html create mode 100644 layout/style/crashtests/873260-2.html create mode 100644 layout/style/crashtests/880862.html create mode 100644 layout/style/crashtests/894245-1.html create mode 100644 layout/style/crashtests/915440.html create mode 100644 layout/style/crashtests/927734-1.html create mode 100644 layout/style/crashtests/930270-1.html create mode 100644 layout/style/crashtests/930270-2.html create mode 100644 layout/style/crashtests/945048-1.html create mode 100644 layout/style/crashtests/972199-1.html create mode 100644 layout/style/crashtests/989965-1.html create mode 100644 layout/style/crashtests/992333-1.html create mode 100644 layout/style/crashtests/blue-32x32.png create mode 100644 layout/style/crashtests/border-image-visited-link.html create mode 100644 layout/style/crashtests/content-only-on-link-before.html create mode 100644 layout/style/crashtests/content-only-on-visited-before.html create mode 100644 layout/style/crashtests/crashtests.list create mode 100644 layout/style/crashtests/font-face-truncated-src.html create mode 100644 layout/style/crashtests/large_border_image_width.html create mode 100644 layout/style/crashtests/link-transition-before.html create mode 100644 layout/style/crashtests/long-url-list-stack-overflow.html create mode 100644 layout/style/crashtests/scale-on-block-continuation.html create mode 100644 layout/style/designmode.css create mode 100644 layout/style/extra-bindgen-flags.in create mode 100644 layout/style/jar.mn create mode 100644 layout/style/moz.build create mode 100644 layout/style/nsAnimationManager.cpp create mode 100644 layout/style/nsAnimationManager.h create mode 100644 layout/style/nsCSSAnonBoxList.h create mode 100644 layout/style/nsCSSAnonBoxes.cpp create mode 100644 layout/style/nsCSSAnonBoxes.h create mode 100644 layout/style/nsCSSCounterDescList.h create mode 100644 layout/style/nsCSSFontDescList.h create mode 100644 layout/style/nsCSSPropertyID.h.in create mode 100644 layout/style/nsCSSPropertyIDSet.h create mode 100644 layout/style/nsCSSProps.cpp create mode 100644 layout/style/nsCSSProps.h create mode 100644 layout/style/nsCSSPseudoElementList.h create mode 100644 layout/style/nsCSSPseudoElements.cpp create mode 100644 layout/style/nsCSSPseudoElements.h create mode 100644 layout/style/nsCSSValue.cpp create mode 100644 layout/style/nsCSSValue.h create mode 100644 layout/style/nsCSSVisitedDependentPropList.h create mode 100644 layout/style/nsComputedDOMStyle.cpp create mode 100644 layout/style/nsComputedDOMStyle.h create mode 100644 layout/style/nsDOMCSSAttrDeclaration.cpp create mode 100644 layout/style/nsDOMCSSAttrDeclaration.h create mode 100644 layout/style/nsDOMCSSDeclaration.cpp create mode 100644 layout/style/nsDOMCSSDeclaration.h create mode 100644 layout/style/nsDOMCSSValueList.cpp create mode 100644 layout/style/nsDOMCSSValueList.h create mode 100644 layout/style/nsFontFaceLoader.cpp create mode 100644 layout/style/nsFontFaceLoader.h create mode 100644 layout/style/nsFontFaceUtils.cpp create mode 100644 layout/style/nsFontFaceUtils.h create mode 100644 layout/style/nsICSSDeclaration.cpp create mode 100644 layout/style/nsICSSDeclaration.h create mode 100644 layout/style/nsICSSLoaderObserver.h create mode 100644 layout/style/nsMediaFeatures.cpp create mode 100644 layout/style/nsROCSSPrimitiveValue.cpp create mode 100644 layout/style/nsROCSSPrimitiveValue.h create mode 100644 layout/style/nsStyleAutoArray.h create mode 100644 layout/style/nsStyleConsts.h create mode 100644 layout/style/nsStyleStruct.cpp create mode 100644 layout/style/nsStyleStruct.h create mode 100644 layout/style/nsStyleStructFwd.h create mode 100644 layout/style/nsStyleStructInlines.h create mode 100644 layout/style/nsStyleStructList.h create mode 100644 layout/style/nsStyleTransformMatrix.cpp create mode 100644 layout/style/nsStyleTransformMatrix.h create mode 100644 layout/style/nsStyleUtil.cpp create mode 100644 layout/style/nsStyleUtil.h create mode 100644 layout/style/nsTransitionManager.cpp create mode 100644 layout/style/nsTransitionManager.h create mode 100644 layout/style/res/Mozilla_Bullet.bf create mode 100644 layout/style/res/Mozilla_Bullet.ttf create mode 100644 layout/style/res/Mozilla_Bullet.woff2 create mode 100644 layout/style/res/accessiblecaret-normal.svg create mode 100644 layout/style/res/accessiblecaret-normal@1.5x.png create mode 100644 layout/style/res/accessiblecaret-normal@1x.png create mode 100644 layout/style/res/accessiblecaret-normal@2.25x.png create mode 100644 layout/style/res/accessiblecaret-normal@2x.png create mode 100644 layout/style/res/accessiblecaret-tilt-left.svg create mode 100644 layout/style/res/accessiblecaret-tilt-left@1.5x.png create mode 100644 layout/style/res/accessiblecaret-tilt-left@1x.png create mode 100644 layout/style/res/accessiblecaret-tilt-left@2.25x.png create mode 100644 layout/style/res/accessiblecaret-tilt-left@2x.png create mode 100644 layout/style/res/accessiblecaret-tilt-right.svg create mode 100644 layout/style/res/accessiblecaret-tilt-right@1.5x.png create mode 100644 layout/style/res/accessiblecaret-tilt-right@1x.png create mode 100644 layout/style/res/accessiblecaret-tilt-right@2.25x.png create mode 100644 layout/style/res/accessiblecaret-tilt-right@2x.png create mode 100644 layout/style/res/accessiblecaret.css create mode 100644 layout/style/res/counterstyles.css create mode 100644 layout/style/res/details.css create mode 100644 layout/style/res/forms.css create mode 100644 layout/style/res/html.css create mode 100644 layout/style/res/noframes.css create mode 100644 layout/style/res/password-hide.svg create mode 100644 layout/style/res/password.svg create mode 100644 layout/style/res/plaintext.css create mode 100644 layout/style/res/quirk.css create mode 100644 layout/style/res/scrollbars.css create mode 100644 layout/style/res/searchfield-cancel.svg create mode 100644 layout/style/res/ua.css create mode 100644 layout/style/res/viewsource.css create mode 100644 layout/style/test/Ahem.ttf create mode 100644 layout/style/test/BitPattern.woff create mode 100644 layout/style/test/ListCSSProperties.cpp create mode 100644 layout/style/test/ParseCSS.cpp create mode 100644 layout/style/test/additional_sheets_helper.html create mode 100644 layout/style/test/animation_utils.js create mode 100644 layout/style/test/browser.toml create mode 100644 layout/style/test/browser_bug453896.js create mode 100644 layout/style/test/browser_sourcemap.js create mode 100644 layout/style/test/browser_sourcemap_comment.js create mode 100644 layout/style/test/browser_sourceurl_comment.js create mode 100644 layout/style/test/bug1382568-iframe.html create mode 100644 layout/style/test/bug1729861.js create mode 100644 layout/style/test/bug453896_iframe.html create mode 100644 layout/style/test/bug517224.sjs create mode 100644 layout/style/test/bug732209-css.sjs create mode 100644 layout/style/test/ccd-quirks.html create mode 100644 layout/style/test/ccd-standards.html create mode 100644 layout/style/test/ccd.sjs create mode 100644 layout/style/test/chrome/bug418986-2.js create mode 100644 layout/style/test/chrome/bug535806-css.css create mode 100644 layout/style/test/chrome/bug535806-html.html create mode 100644 layout/style/test/chrome/bug535806-xul.xhtml create mode 100644 layout/style/test/chrome/chrome-only-media-queries.js create mode 100644 layout/style/test/chrome/chrome.toml create mode 100644 layout/style/test/chrome/display_mode.html create mode 100644 layout/style/test/chrome/display_mode_reflow.html create mode 100644 layout/style/test/chrome/display_mode_reflow_iframe.html create mode 100644 layout/style/test/chrome/hover_empty.html create mode 100644 layout/style/test/chrome/hover_helper.html create mode 100644 layout/style/test/chrome/import_useless1.css create mode 100644 layout/style/test/chrome/import_useless2.css create mode 100644 layout/style/test/chrome/match.png create mode 100644 layout/style/test/chrome/mismatch.png create mode 100644 layout/style/test/chrome/moz_document_helper.html create mode 100644 layout/style/test/chrome/test_bug1157097.html create mode 100644 layout/style/test/chrome/test_bug1346623.html create mode 100644 layout/style/test/chrome/test_bug1371453.html create mode 100644 layout/style/test/chrome/test_bug418986-2.xhtml create mode 100644 layout/style/test/chrome/test_bug511909.html create mode 100644 layout/style/test/chrome/test_bug535806.xhtml create mode 100644 layout/style/test/chrome/test_chrome_only_media_queries.html create mode 100644 layout/style/test/chrome/test_constructable_stylesheets_chrome_only_rules.html create mode 100644 layout/style/test/chrome/test_display_mode.html create mode 100644 layout/style/test/chrome/test_display_mode_reflow.html create mode 100644 layout/style/test/chrome/test_hover.html create mode 100644 layout/style/test/chrome/test_moz_document_rules.html create mode 100644 layout/style/test/chrome/test_moz_document_serialization.html create mode 100644 layout/style/test/chrome/test_scrollbar_inline_size.html create mode 100644 layout/style/test/chrome/test_stylesheet_clone_import_rule.html create mode 100644 layout/style/test/css_properties_like_longhand.js create mode 100644 layout/style/test/descriptor_database.js create mode 100644 layout/style/test/empty.html create mode 100644 layout/style/test/file_animations_async_tests.html create mode 100644 layout/style/test/file_animations_omta_scroll.html create mode 100644 layout/style/test/file_animations_omta_scroll_rtl.html create mode 100644 layout/style/test/file_animations_with_disabled_properties.html create mode 100644 layout/style/test/file_bug1055933_circle-xxl.png create mode 100644 layout/style/test/file_bug1089417_iframe.html create mode 100644 layout/style/test/file_bug1375944.html create mode 100644 layout/style/test/file_bug1381233.html create mode 100644 layout/style/test/file_bug1443344.css create mode 100644 layout/style/test/file_bug645998-1.css create mode 100644 layout/style/test/file_bug645998-2.css create mode 100644 layout/style/test/file_bug829816.css create mode 100644 layout/style/test/file_computed_style_bfcache_display_none.html create mode 100644 layout/style/test/file_computed_style_bfcache_display_none2.html create mode 100644 layout/style/test/file_font_loading_api_vframe.html create mode 100644 layout/style/test/file_shared_sheet_caching.css create mode 100644 layout/style/test/file_shared_sheet_caching.html create mode 100644 layout/style/test/file_specified_value_serialization_individual_transforms.html create mode 100644 layout/style/test/flexbox_layout_testcases.js create mode 100644 layout/style/test/gen-css-properties.py create mode 100644 layout/style/test/gtest/ImportScannerTest.cpp create mode 100644 layout/style/test/gtest/StyloParsingBench.cpp create mode 100644 layout/style/test/gtest/example.css create mode 100644 layout/style/test/gtest/generate_example_stylesheet.py create mode 100644 layout/style/test/gtest/moz.build create mode 100644 layout/style/test/mapped.css create mode 100644 layout/style/test/mapped.css^headers^ create mode 100644 layout/style/test/mapped2.css create mode 100644 layout/style/test/mapped2.css^headers^ create mode 100644 layout/style/test/media_queries_iframe.html create mode 100644 layout/style/test/media_queries_iframe2.html create mode 100644 layout/style/test/mochitest.toml create mode 100644 layout/style/test/moz.build create mode 100644 layout/style/test/mq_changes_child.html create mode 100644 layout/style/test/neverending_font_load.sjs create mode 100644 layout/style/test/neverending_stylesheet_load.sjs create mode 100644 layout/style/test/post-redirect-1.css create mode 100644 layout/style/test/post-redirect-2.css create mode 100644 layout/style/test/post-redirect-3.css create mode 100644 layout/style/test/property_database.js create mode 100644 layout/style/test/redirect.sjs create mode 100644 layout/style/test/redundant_font_download.sjs create mode 100644 layout/style/test/slow_broken_sheet.sjs create mode 100644 layout/style/test/slow_load.sjs create mode 100644 layout/style/test/slow_ok_sheet.sjs create mode 100644 layout/style/test/sourcemap_css.html create mode 100644 layout/style/test/style_attribute_tests.js create mode 100644 layout/style/test/support/1x1-transparent.png create mode 100644 layout/style/test/support/blue-100x100.png create mode 100644 layout/style/test/support/external-variable-url.css create mode 100644 layout/style/test/test_acid3_test46.html create mode 100644 layout/style/test/test_addSheet.html create mode 100644 layout/style/test/test_additional_sheets.html create mode 100644 layout/style/test/test_align_justify_computed_values.html create mode 100644 layout/style/test/test_all_shorthand.html create mode 100644 layout/style/test/test_animations.html create mode 100644 layout/style/test/test_animations_async_tests.html create mode 100644 layout/style/test/test_animations_dynamic_changes.html create mode 100644 layout/style/test/test_animations_effect_timing_duration.html create mode 100644 layout/style/test/test_animations_effect_timing_enddelay.html create mode 100644 layout/style/test/test_animations_effect_timing_iterations.html create mode 100644 layout/style/test/test_animations_event_handler_attribute.html create mode 100644 layout/style/test/test_animations_event_order.html create mode 100644 layout/style/test/test_animations_iterationstart.html create mode 100644 layout/style/test/test_animations_omta.html create mode 100644 layout/style/test/test_animations_omta_scroll.html create mode 100644 layout/style/test/test_animations_omta_scroll_rtl.html create mode 100644 layout/style/test/test_animations_omta_start.html create mode 100644 layout/style/test/test_animations_pausing.html create mode 100644 layout/style/test/test_animations_playbackrate.html create mode 100644 layout/style/test/test_animations_reverse.html create mode 100644 layout/style/test/test_animations_styles_on_event.html create mode 100644 layout/style/test/test_animations_variable_changes.html create mode 100644 layout/style/test/test_animations_with_disabled_properties.html create mode 100644 layout/style/test/test_any_dynamic.html create mode 100644 layout/style/test/test_area_url_cursor.html create mode 100644 layout/style/test/test_asyncopen.html create mode 100644 layout/style/test/test_at_rule_parse_serialize.html create mode 100644 layout/style/test/test_attribute_selector_eof_behavior.html create mode 100644 layout/style/test/test_backdrop_filter_enabled_state.html create mode 100644 layout/style/test/test_background_blend_mode.html create mode 100644 layout/style/test/test_border_device_pixel_rounding_initial_style.html create mode 100644 layout/style/test/test_box_size_keywords.html create mode 100644 layout/style/test/test_bug1055933.html create mode 100644 layout/style/test/test_bug1089417.html create mode 100644 layout/style/test/test_bug1112014.html create mode 100644 layout/style/test/test_bug1203766.html create mode 100644 layout/style/test/test_bug1232829.html create mode 100644 layout/style/test/test_bug1292447.html create mode 100644 layout/style/test/test_bug1330375.html create mode 100644 layout/style/test/test_bug1371488.html create mode 100644 layout/style/test/test_bug1375944.html create mode 100644 layout/style/test/test_bug1382568.html create mode 100644 layout/style/test/test_bug1394302.html create mode 100644 layout/style/test/test_bug1443344-1.html create mode 100644 layout/style/test/test_bug1443344-2.html create mode 100644 layout/style/test/test_bug1451199-1.html create mode 100644 layout/style/test/test_bug1451199-2.html create mode 100644 layout/style/test/test_bug1490890.html create mode 100644 layout/style/test/test_bug1505254.html create mode 100644 layout/style/test/test_bug160403.html create mode 100644 layout/style/test/test_bug1729861.html create mode 100644 layout/style/test/test_bug200089.html create mode 100644 layout/style/test/test_bug221428.html create mode 100644 layout/style/test/test_bug229915.html create mode 100644 layout/style/test/test_bug302186.html create mode 100644 layout/style/test/test_bug319381.html create mode 100644 layout/style/test/test_bug357614.html create mode 100644 layout/style/test/test_bug363146.html create mode 100644 layout/style/test/test_bug372770.html create mode 100644 layout/style/test/test_bug373293.html create mode 100644 layout/style/test/test_bug377947.html create mode 100644 layout/style/test/test_bug379440.html create mode 100644 layout/style/test/test_bug379741.html create mode 100644 layout/style/test/test_bug382027.html create mode 100644 layout/style/test/test_bug383075.html create mode 100644 layout/style/test/test_bug387615.html create mode 100644 layout/style/test/test_bug389464.html create mode 100644 layout/style/test/test_bug391034.html create mode 100644 layout/style/test/test_bug391221.html create mode 100644 layout/style/test/test_bug397427.html create mode 100644 layout/style/test/test_bug399349.html create mode 100644 layout/style/test/test_bug401046.html create mode 100644 layout/style/test/test_bug405818.html create mode 100644 layout/style/test/test_bug412901.html create mode 100644 layout/style/test/test_bug413958.html create mode 100644 layout/style/test/test_bug418986-2.html create mode 100644 layout/style/test/test_bug437915.html create mode 100644 layout/style/test/test_bug450191.html create mode 100644 layout/style/test/test_bug470769.html create mode 100644 layout/style/test/test_bug499655.html create mode 100644 layout/style/test/test_bug499655.xhtml create mode 100644 layout/style/test/test_bug517224.html create mode 100644 layout/style/test/test_bug524175.html create mode 100644 layout/style/test/test_bug525952.html create mode 100644 layout/style/test/test_bug534804.html create mode 100644 layout/style/test/test_bug573255.html create mode 100644 layout/style/test/test_bug580685.html create mode 100644 layout/style/test/test_bug621351.html create mode 100644 layout/style/test/test_bug635286.html create mode 100644 layout/style/test/test_bug645998.html create mode 100644 layout/style/test/test_bug652486.html create mode 100644 layout/style/test/test_bug657143.html create mode 100644 layout/style/test/test_bug667520.html create mode 100644 layout/style/test/test_bug716226.html create mode 100644 layout/style/test/test_bug732153.html create mode 100644 layout/style/test/test_bug732209.html create mode 100644 layout/style/test/test_bug73586.html create mode 100644 layout/style/test/test_bug74880.html create mode 100644 layout/style/test/test_bug765590.html create mode 100644 layout/style/test/test_bug771043.html create mode 100644 layout/style/test/test_bug795520.html create mode 100644 layout/style/test/test_bug798843_pref.html create mode 100644 layout/style/test/test_bug829816.html create mode 100644 layout/style/test/test_bug874919.html create mode 100644 layout/style/test/test_bug887741_at-rules_in_declaration_lists.html create mode 100644 layout/style/test/test_bug892929.html create mode 100644 layout/style/test/test_bug98997.html create mode 100644 layout/style/test/test_cascade.html create mode 100644 layout/style/test/test_ch_ex_no_infloops.html create mode 100644 layout/style/test/test_change_hint_optimizations.html create mode 100644 layout/style/test/test_clip-path_polygon.html create mode 100644 layout/style/test/test_color_rounding.html create mode 100644 layout/style/test/test_compute_data_with_start_struct.html create mode 100644 layout/style/test/test_computed_style.html create mode 100644 layout/style/test/test_computed_style_bfcache_display_none.html create mode 100644 layout/style/test/test_computed_style_difference.html create mode 100644 layout/style/test/test_computed_style_grid_with_pseudo.html create mode 100644 layout/style/test/test_computed_style_in_created_document.html create mode 100644 layout/style/test/test_computed_style_min_size_auto.html create mode 100644 layout/style/test/test_computed_style_no_flush.html create mode 100644 layout/style/test/test_computed_style_no_pseudo.html create mode 100644 layout/style/test/test_computed_style_prefs.html create mode 100644 layout/style/test/test_condition_text.html create mode 100644 layout/style/test/test_constructable_stylesheets_chrome_only_rules_in_content.html create mode 100644 layout/style/test/test_counter_descriptor_storage.html create mode 100644 layout/style/test/test_counter_style.html create mode 100644 layout/style/test/test_crash_with_content_policy.html create mode 100644 layout/style/test/test_css_cross_domain.html create mode 100644 layout/style/test/test_css_cross_domain_no_orb.html create mode 100644 layout/style/test/test_css_eof_handling.html create mode 100644 layout/style/test/test_css_escape_api.html create mode 100644 layout/style/test/test_css_function_mismatched_parenthesis.html create mode 100644 layout/style/test/test_css_loader_crossorigin_data_url.html create mode 100644 layout/style/test/test_css_parse_error_smoketest.html create mode 100644 layout/style/test/test_css_supports.html create mode 100644 layout/style/test/test_css_supports_variables.html create mode 100644 layout/style/test/test_cue_restrictions.html create mode 100644 layout/style/test/test_custom_content_inheritance.html create mode 100644 layout/style/test/test_default_bidi_css.html create mode 100644 layout/style/test/test_default_computed_style.html create mode 100644 layout/style/test/test_descriptor_storage.html create mode 100644 layout/style/test/test_descriptor_syntax_errors.html create mode 100644 layout/style/test/test_display_mode.html create mode 100644 layout/style/test/test_dont_use_document_colors.html create mode 100644 layout/style/test/test_dont_use_document_fonts.html create mode 100644 layout/style/test/test_dynamic_change_causing_reflow.html create mode 100644 layout/style/test/test_exposed_prop_accessors.html create mode 100644 layout/style/test/test_extra_inherit_initial.html create mode 100644 layout/style/test/test_first_letter_restrictions.html create mode 100644 layout/style/test/test_first_line_restrictions.html create mode 100644 layout/style/test/test_flexbox_child_display_values.xhtml create mode 100644 layout/style/test/test_flexbox_flex_grow_and_shrink.html create mode 100644 layout/style/test/test_flexbox_flex_shorthand.html create mode 100644 layout/style/test/test_flexbox_focus_order.html create mode 100644 layout/style/test/test_flexbox_layout.html create mode 100644 layout/style/test/test_flexbox_order.html create mode 100644 layout/style/test/test_flexbox_order_abspos.html create mode 100644 layout/style/test/test_flexbox_order_table.html create mode 100644 layout/style/test/test_flexbox_reflow_counts.html create mode 100644 layout/style/test/test_flushing_frame.html create mode 100644 layout/style/test/test_font_face_cascade.html create mode 100644 layout/style/test/test_font_face_parser.html create mode 100644 layout/style/test/test_font_family_parsing.html create mode 100644 layout/style/test/test_font_family_serialization.html create mode 100644 layout/style/test/test_font_loading_api.html create mode 100644 layout/style/test/test_garbage_at_end_of_declarations.html create mode 100644 layout/style/test/test_grid_computed_values.html create mode 100644 layout/style/test/test_grid_container_shorthands.html create mode 100644 layout/style/test/test_grid_item_shorthands.html create mode 100644 layout/style/test/test_grid_shorthand_serialization.html create mode 100644 layout/style/test/test_group_insertRule.html create mode 100644 layout/style/test/test_hover_on_part.html create mode 100644 layout/style/test/test_hover_quirk.html create mode 100644 layout/style/test/test_html_attribute_computed_values.html create mode 100644 layout/style/test/test_ident_escaping.html create mode 100644 layout/style/test/test_img_src_causing_reflow.html create mode 100644 layout/style/test/test_import_preload.html create mode 100644 layout/style/test/test_inherit_computation.html create mode 100644 layout/style/test/test_inherit_storage.html create mode 100644 layout/style/test/test_initial_computation.html create mode 100644 layout/style/test/test_initial_storage.html create mode 100644 layout/style/test/test_invalidation_basic.html create mode 100644 layout/style/test/test_keyframes_rules.html create mode 100644 layout/style/test/test_keyframes_vendor_prefix.html create mode 100644 layout/style/test/test_load_events_on_stylesheets.html create mode 100644 layout/style/test/test_logical_properties.html create mode 100644 layout/style/test/test_marker_restrictions.html create mode 100644 layout/style/test/test_mask_image_CORS.html create mode 100644 layout/style/test/test_media_queries.html create mode 100644 layout/style/test/test_media_queries_dynamic.html create mode 100644 layout/style/test/test_media_query_list.html create mode 100644 layout/style/test/test_media_query_serialization.html create mode 100644 layout/style/test/test_moz_device_pixel_ratio.html create mode 100644 layout/style/test/test_moz_prefixed_cursor.html create mode 100644 layout/style/test/test_mq_any_hover_and_any_pointer.html create mode 100644 layout/style/test/test_mq_changes_in_iframe.html create mode 100644 layout/style/test/test_mq_hover_and_pointer.html create mode 100644 layout/style/test/test_mq_prefers_contrast_dynamic.html create mode 100644 layout/style/test/test_mq_prefers_reduced_motion_dynamic.html create mode 100644 layout/style/test/test_mql_event_listener_leaks.html create mode 100644 layout/style/test/test_namespace_rule.html create mode 100644 layout/style/test/test_non_content_accessible_env_vars.html create mode 100644 layout/style/test/test_non_content_accessible_properties.html create mode 100644 layout/style/test/test_non_content_accessible_pseudos.html create mode 100644 layout/style/test/test_non_content_accessible_values.html create mode 100644 layout/style/test/test_non_matching_sheet_media.html create mode 100644 layout/style/test/test_of_type_selectors.xhtml create mode 100644 layout/style/test/test_overscroll_behavior_pref.html create mode 100644 layout/style/test/test_page_parser.html create mode 100644 layout/style/test/test_parse_eof.html create mode 100644 layout/style/test/test_parse_ident.html create mode 100644 layout/style/test/test_parse_rule.html create mode 100644 layout/style/test/test_parse_url.html create mode 100644 layout/style/test/test_parser_diagnostics_unprintables.html create mode 100644 layout/style/test/test_pixel_lengths.html create mode 100644 layout/style/test/test_placeholder_restrictions.html create mode 100644 layout/style/test/test_pointer-events.html create mode 100644 layout/style/test/test_position_float_display.html create mode 100644 layout/style/test/test_position_sticky.html create mode 100644 layout/style/test/test_prefers_contrast_color_pairs.html create mode 100644 layout/style/test/test_priority_preservation.html create mode 100644 layout/style/test/test_property_database.html create mode 100644 layout/style/test/test_property_syntax_errors.html create mode 100644 layout/style/test/test_pseudo_display_fixup.html create mode 100644 layout/style/test/test_pseudoelement_parsing.html create mode 100644 layout/style/test/test_pseudoelement_state.html create mode 100644 layout/style/test/test_query_container_for.html create mode 100644 layout/style/test/test_redundant_font_download.html create mode 100644 layout/style/test/test_reframe_cb.html create mode 100644 layout/style/test/test_reframe_image_loading.html create mode 100644 layout/style/test/test_reframe_input.html create mode 100644 layout/style/test/test_reframe_pseudo_element.html create mode 100644 layout/style/test/test_rem_unit.html create mode 100644 layout/style/test/test_restyle_table_wrapper.html create mode 100644 layout/style/test/test_restyles_in_smil_animation.html create mode 100644 layout/style/test/test_revert.html create mode 100644 layout/style/test/test_root_node_display.html create mode 100644 layout/style/test/test_rule_insertion.html create mode 100644 layout/style/test/test_rules_out_of_sheets.html create mode 100644 layout/style/test/test_selectors.html create mode 100644 layout/style/test/test_setPropertyWithNull.html create mode 100644 layout/style/test/test_shape_outside_CORS.html create mode 100644 layout/style/test/test_shared_sheet_caching.html create mode 100644 layout/style/test/test_sheet_privilege.html create mode 100644 layout/style/test/test_shorthand_property_getters.html create mode 100644 layout/style/test/test_specified_value_serialization.html create mode 100644 layout/style/test/test_style_attr_listener.html create mode 100644 layout/style/test/test_style_attribute_quirks.html create mode 100644 layout/style/test/test_style_attribute_standards.html create mode 100644 layout/style/test/test_style_struct_copy_constructors.html create mode 100644 layout/style/test/test_stylesheet_additions.html create mode 100644 layout/style/test/test_stylesheet_clone_font_face.html create mode 100644 layout/style/test/test_supports_rules.html create mode 100644 layout/style/test/test_system_font_serialization.html create mode 100644 layout/style/test/test_text_decoration_shorthands.html create mode 100644 layout/style/test/test_transitions.html create mode 100644 layout/style/test/test_transitions_and_reframes.html create mode 100644 layout/style/test/test_transitions_and_restyles.html create mode 100644 layout/style/test/test_transitions_and_zoom.html create mode 100644 layout/style/test/test_transitions_at_start.html create mode 100644 layout/style/test/test_transitions_bug537151.html create mode 100644 layout/style/test/test_transitions_cancel_near_end.html create mode 100644 layout/style/test/test_transitions_computed_value_combinations.html create mode 100644 layout/style/test/test_transitions_computed_values.html create mode 100644 layout/style/test/test_transitions_dynamic_changes.html create mode 100644 layout/style/test/test_transitions_events.html create mode 100644 layout/style/test/test_transitions_per_property.html create mode 100644 layout/style/test/test_transitions_replacement_on_busy_frame.html create mode 100644 layout/style/test/test_transitions_replacement_with_setKeyframes.html create mode 100644 layout/style/test/test_transitions_step_functions.html create mode 100644 layout/style/test/test_unclosed_parentheses.html create mode 100644 layout/style/test/test_unicode_range_loading.html create mode 100644 layout/style/test/test_units_angle.html create mode 100644 layout/style/test/test_units_frequency.html create mode 100644 layout/style/test/test_units_length.html create mode 100644 layout/style/test/test_units_time.html create mode 100644 layout/style/test/test_use_counters.html create mode 100644 layout/style/test/test_user_sheet_shadow_dom.html create mode 100644 layout/style/test/test_value_cloning.html create mode 100644 layout/style/test/test_value_computation.html create mode 100644 layout/style/test/test_value_storage.html create mode 100644 layout/style/test/test_variable_serialization_computed.html create mode 100644 layout/style/test/test_variable_serialization_specified.html create mode 100644 layout/style/test/test_variables.html create mode 100644 layout/style/test/test_variables_loop.html create mode 100644 layout/style/test/test_variables_order.html create mode 100644 layout/style/test/test_video_object_fit.html create mode 100644 layout/style/test/test_viewport_scrollbar_causing_reflow.html create mode 100644 layout/style/test/test_viewport_units.html create mode 100644 layout/style/test/test_visited_image_loading.html create mode 100644 layout/style/test/test_visited_image_loading_empty.html create mode 100644 layout/style/test/test_visited_lying.html create mode 100644 layout/style/test/test_visited_pref.html create mode 100644 layout/style/test/test_visited_reftests.html create mode 100644 layout/style/test/test_webkit_device_pixel_ratio.html create mode 100644 layout/style/test/test_webkit_flex_display.html create mode 100644 layout/style/test/unstyled-frame.css create mode 100644 layout/style/test/unstyled-frame.xml create mode 100644 layout/style/test/unstyled.css create mode 100644 layout/style/test/unstyled.xml create mode 100644 layout/style/test/viewport_units_iframe.html create mode 100644 layout/style/test/visited-lying-inner.html create mode 100644 layout/style/test/visited-pref-iframe.html create mode 100644 layout/style/test/visited_image_loading.sjs create mode 100644 layout/style/test/visited_image_loading_frame.html create mode 100644 layout/style/test/visited_image_loading_frame_empty.html create mode 100644 layout/style/tools/cleanup_computed_getters.py (limited to 'layout/style') diff --git a/layout/style/AnimatedPropertyID.h b/layout/style/AnimatedPropertyID.h new file mode 100644 index 0000000000..f532e72122 --- /dev/null +++ b/layout/style/AnimatedPropertyID.h @@ -0,0 +1,94 @@ +/* -*- 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_AnimatedPropertyID_h +#define mozilla_AnimatedPropertyID_h + +#include "nsCSSPropertyID.h" +#include "nsCSSProps.h" +#include "nsString.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla { + +struct AnimatedPropertyID { + explicit AnimatedPropertyID(nsCSSPropertyID aProperty) : mID(aProperty) { + MOZ_ASSERT(aProperty != eCSSPropertyExtra_variable, + "Cannot create an AnimatedPropertyID from only a " + "eCSSPropertyExtra_variable."); + } + + explicit AnimatedPropertyID(RefPtr aCustomName) + : mID(eCSSPropertyExtra_variable), mCustomName(std::move(aCustomName)) { + MOZ_ASSERT(mCustomName, "Null custom property name"); + } + + nsCSSPropertyID mID = eCSSProperty_UNKNOWN; + RefPtr mCustomName; + + bool IsCustom() const { return mID == eCSSPropertyExtra_variable; } + bool operator==(const AnimatedPropertyID& aOther) const { + return mID == aOther.mID && mCustomName == aOther.mCustomName; + } + bool operator!=(const AnimatedPropertyID& aOther) const { + return !(*this == aOther); + } + + bool IsValid() const { + if (mID == eCSSProperty_UNKNOWN) { + return false; + } + return IsCustom() == !!mCustomName; + } + + void ToString(nsACString& aString) const { + if (IsCustom()) { + MOZ_ASSERT(mCustomName, "Custom property should have a name"); + // mCustomName does not include the "--" prefix + aString.AssignLiteral("--"); + AppendUTF16toUTF8(nsDependentAtomString(mCustomName), aString); + } else { + aString.Assign(nsCSSProps::GetStringValue(mID)); + } + } + + void ToString(nsAString& aString) const { + if (IsCustom()) { + MOZ_ASSERT(mCustomName, "Custom property should have a name"); + // mCustomName does not include the "--" prefix + aString.AssignLiteral("--"); + aString.Append(nsDependentAtomString(mCustomName)); + } else { + aString.Assign(NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(mID))); + } + } + + HashNumber Hash() const { + HashNumber hash = mCustomName ? mCustomName->hash() : 0; + return AddToHash(hash, mID); + } + + AnimatedPropertyID ToPhysical(const ComputedStyle& aStyle) const { + if (IsCustom()) { + return *this; + } + return AnimatedPropertyID{nsCSSProps::Physicalize(mID, aStyle)}; + } +}; + +// MOZ_DBG support for AnimatedPropertyId +inline std::ostream& operator<<(std::ostream& aOut, + const AnimatedPropertyID& aProperty) { + if (aProperty.IsCustom()) { + return aOut << nsAtomCString(aProperty.mCustomName); + } + return aOut << nsCSSProps::GetStringValue(aProperty.mID); +} + +} // namespace mozilla + +#endif // mozilla_AnimatedPropertyID_h diff --git a/layout/style/AnimatedPropertyIDSet.h b/layout/style/AnimatedPropertyIDSet.h new file mode 100644 index 0000000000..667f081421 --- /dev/null +++ b/layout/style/AnimatedPropertyIDSet.h @@ -0,0 +1,149 @@ +/* -*- 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_AnimatedPropertyIDSet_h +#define mozilla_AnimatedPropertyIDSet_h + +#include "mozilla/ServoBindings.h" + +#include "AnimatedPropertyID.h" +#include "nsCSSPropertyIDSet.h" +#include "nsTHashSet.h" + +namespace mozilla { + +class AnimatedPropertyIDSet { + public: + void AddProperty(const AnimatedPropertyID& aProperty) { + if (aProperty.IsCustom()) { + mCustomNames.Insert(aProperty.mCustomName); + } else { + mIDs.AddProperty(aProperty.mID); + } + } + + void RemoveProperty(const AnimatedPropertyID& aProperty) { + if (aProperty.IsCustom()) { + mCustomNames.Remove(aProperty.mCustomName); + } else { + mIDs.RemoveProperty(aProperty.mID); + } + } + + bool HasProperty(const AnimatedPropertyID& aProperty) const { + if (aProperty.IsCustom()) { + return mCustomNames.Contains(aProperty.mCustomName); + } + return mIDs.HasProperty(aProperty.mID); + } + + bool Intersects(const nsCSSPropertyIDSet& aIDs) const { + return mIDs.Intersects(aIDs); + } + + bool IsSubsetOf(const AnimatedPropertyIDSet& aOther) const { + if (!mIDs.IsSubsetOf(aOther.mIDs) || + mCustomNames.Count() > aOther.mCustomNames.Count()) { + return false; + } + for (const auto& name : mCustomNames) { + if (!aOther.mCustomNames.Contains(name)) { + return false; + } + } + return true; + } + + void AddProperties(const AnimatedPropertyIDSet& aOther) { + mIDs |= aOther.mIDs; + for (const auto& name : aOther.mCustomNames) { + mCustomNames.Insert(name); + } + } + + using CustomNameSet = nsTHashSet>; + + // Iterator for use in range-based for loops + class Iterator { + public: + Iterator(Iterator&& aOther) + : mPropertySet(aOther.mPropertySet), + mIDIterator(std::move(aOther.mIDIterator)), + mCustomNameIterator(std::move(aOther.mCustomNameIterator)), + mPropertyID(eCSSProperty_UNKNOWN) {} + Iterator() = delete; + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + Iterator& operator=(const Iterator&&) = delete; + + static Iterator BeginIterator(const AnimatedPropertyIDSet& aPropertySet) { + return Iterator(aPropertySet, aPropertySet.mIDs.begin(), + aPropertySet.mCustomNames.begin()); + } + + static Iterator EndIterator(const AnimatedPropertyIDSet& aPropertySet) { + return Iterator(aPropertySet, aPropertySet.mIDs.end(), + aPropertySet.mCustomNames.end()); + } + + bool operator!=(const Iterator& aOther) const { + return mIDIterator != aOther.mIDIterator || + mCustomNameIterator != aOther.mCustomNameIterator; + } + + Iterator& operator++() { + MOZ_ASSERT(mIDIterator != mPropertySet.mIDs.end() || + mCustomNameIterator != mPropertySet.mCustomNames.end(), + "Should not iterate beyond end"); + if (mIDIterator != mPropertySet.mIDs.end()) { + ++mIDIterator; + } else { + ++mCustomNameIterator; + } + return *this; + } + + AnimatedPropertyID operator*() { + if (mIDIterator != mPropertySet.mIDs.end()) { + mPropertyID.mID = *mIDIterator; + mPropertyID.mCustomName = nullptr; + } else if (mCustomNameIterator != mPropertySet.mCustomNames.end()) { + mPropertyID.mID = eCSSPropertyExtra_variable; + mPropertyID.mCustomName = *mCustomNameIterator; + } else { + MOZ_ASSERT_UNREACHABLE("Should not dereference beyond end"); + mPropertyID.mID = eCSSProperty_UNKNOWN; + mPropertyID.mCustomName = nullptr; + } + return mPropertyID; + } + + private: + Iterator(const AnimatedPropertyIDSet& aPropertySet, + nsCSSPropertyIDSet::Iterator aIDIterator, + CustomNameSet::const_iterator aCustomNameIterator) + : mPropertySet(aPropertySet), + mIDIterator(std::move(aIDIterator)), + mCustomNameIterator(std::move(aCustomNameIterator)), + mPropertyID(eCSSProperty_UNKNOWN) {} + + const AnimatedPropertyIDSet& mPropertySet; + nsCSSPropertyIDSet::Iterator mIDIterator; + CustomNameSet::const_iterator mCustomNameIterator; + AnimatedPropertyID mPropertyID; + }; + + Iterator begin() const { return Iterator::BeginIterator(*this); } + Iterator end() const { return Iterator::EndIterator(*this); } + + private: + nsCSSPropertyIDSet mIDs; + CustomNameSet mCustomNames; +}; + +} // namespace mozilla + +#endif // mozilla_AnimatedPropertyIDSet_h diff --git a/layout/style/AnimationCollection.cpp b/layout/style/AnimationCollection.cpp new file mode 100644 index 0000000000..69d555fd2d --- /dev/null +++ b/layout/style/AnimationCollection.cpp @@ -0,0 +1,79 @@ +/* -*- 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 "mozilla/AnimationCollection.h" +#include + +#include "mozilla/ElementAnimationData.h" +#include "mozilla/RestyleManager.h" +#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch +#include "mozilla/dom/CSSAnimation.h" // For dom::CSSAnimation +#include "mozilla/dom/CSSTransition.h" // For dom::CSSTransition + +namespace mozilla { + +template +AnimationCollection::~AnimationCollection() { + MOZ_COUNT_DTOR(AnimationCollection); + + const PostRestyleMode postRestyle = + mCalledDestroy ? PostRestyleMode::IfNeeded : PostRestyleMode::Never; + { + nsAutoAnimationMutationBatch mb(mElement.OwnerDoc()); + + for (size_t animIdx = mAnimations.Length(); animIdx-- != 0;) { + mAnimations[animIdx]->CancelFromStyle(postRestyle); + } + } + LinkedListElement::remove(); +} + +template +void AnimationCollection::Destroy() { + mCalledDestroy = true; + auto* data = mElement.GetAnimationData(); + MOZ_ASSERT(data); + if constexpr (std::is_same_v) { + MOZ_ASSERT(data->GetAnimationCollection(mPseudo) == this); + data->ClearAnimationCollectionFor(mPseudo); + } else { + MOZ_ASSERT(data->GetTransitionCollection(mPseudo) == this); + data->ClearTransitionCollectionFor(mPseudo); + } +} + +template +AnimationCollection* AnimationCollection::Get( + const dom::Element* aElement, const PseudoStyleType aType) { + auto* data = aElement->GetAnimationData(); + if (!data) { + return nullptr; + } + if constexpr (std::is_same_v) { + return data->GetAnimationCollection(aType); + } else { + return data->GetTransitionCollection(aType); + } +} + +template +/* static */ AnimationCollection* +AnimationCollection::Get(const nsIFrame* aFrame) { + Maybe target = + EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame); + if (!target) { + return nullptr; + } + + return Get(target->mElement, target->mPseudoType); +} + +// Explicit class instantiations + +template class AnimationCollection; +template class AnimationCollection; + +} // namespace mozilla diff --git a/layout/style/AnimationCollection.h b/layout/style/AnimationCollection.h new file mode 100644 index 0000000000..84f68506d1 --- /dev/null +++ b/layout/style/AnimationCollection.h @@ -0,0 +1,71 @@ +/* -*- 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_AnimationCollection_h +#define mozilla_AnimationCollection_h + +#include "mozilla/Assertions.h" +#include "mozilla/LinkedList.h" +#include "mozilla/RefPtr.h" +#include "nsCSSPseudoElements.h" +#include "nsTArrayForwardDeclare.h" + +class nsAtom; +class nsIFrame; +class nsPresContext; + +namespace mozilla { +namespace dom { +class Element; +} +enum class PseudoStyleType : uint8_t; + +template +class AnimationCollection + : public LinkedListElement> { + typedef AnimationCollection SelfType; + + public: + AnimationCollection(dom::Element& aOwner, PseudoStyleType aPseudoType) + : mElement(aOwner), mPseudo(aPseudoType) { + MOZ_COUNT_CTOR(AnimationCollection); + } + + ~AnimationCollection(); + + void Destroy(); + + // Given the frame |aFrame| with possibly animated content, finds its + // associated collection of animations. If |aFrame| is a generated content + // frame, this function may examine the parent frame to search for such + // animations. + static AnimationCollection* Get(const nsIFrame* aFrame); + static AnimationCollection* Get(const dom::Element* aElement, + PseudoStyleType aPseudoType); + + // The element. Weak reference is fine since it owns us. + // FIXME(emilio): These are only needed for Destroy(), so maybe remove and + // rely on the caller clearing us properly? + dom::Element& mElement; + const PseudoStyleType mPseudo; + + nsTArray> mAnimations; + + private: + // We distinguish between destroying this by calling Destroy() vs directly + // clearing the collection. + // + // The former case represents regular updating due to style changes and should + // trigger subsequent restyles. + // + // The latter case represents document tear-down or other DOM surgery in + // which case we should not trigger restyles. + bool mCalledDestroy = false; +}; + +} // namespace mozilla + +#endif // mozilla_AnimationCollection_h diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h new file mode 100644 index 0000000000..d7a54c9751 --- /dev/null +++ b/layout/style/AnimationCommon.h @@ -0,0 +1,225 @@ +/* -*- 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_css_AnimationCommon_h +#define mozilla_css_AnimationCommon_h + +#include "mozilla/AnimationCollection.h" +#include "mozilla/LinkedList.h" +#include "mozilla/dom/Animation.h" +#include "mozilla/dom/BaseKeyframeTypesBinding.h" +#include "mozilla/Assertions.h" +#include "mozilla/Maybe.h" +#include "mozilla/TimingParams.h" +#include "mozilla/dom/Nullable.h" +#include "nsContentUtils.h" +#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch + +class nsPresContext; + +namespace mozilla { +enum class PseudoStyleType : uint8_t; + +namespace dom { +class Element; +} + +template +class CommonAnimationManager { + public: + explicit CommonAnimationManager(nsPresContext* aPresContext) + : mPresContext(aPresContext) {} + + // NOTE: This can return null after Disconnect(). + nsPresContext* PresContext() const { return mPresContext; } + + /** + * Notify the manager that the pres context is going away. + */ + void Disconnect() { + // Content nodes might outlive the transition or animation manager. + RemoveAllElementCollections(); + + mPresContext = nullptr; + } + + /** + * Stop animations on the element. This method takes the real element + * rather than the element for the generated content for animations on + * ::before, ::after and ::marker. + */ + void StopAnimationsForElement(dom::Element* aElement, + PseudoStyleType aPseudoType) { + MOZ_ASSERT(aElement); + auto* collection = + AnimationCollection::Get(aElement, aPseudoType); + if (!collection) { + return; + } + + nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); + collection->Destroy(); + } + + protected: + virtual ~CommonAnimationManager() { + MOZ_ASSERT(!mPresContext, "Disconnect should have been called"); + } + + void AddElementCollection(AnimationCollection* aCollection) { + mElementCollections.insertBack(aCollection); + } + void RemoveAllElementCollections() { + while (AnimationCollection* head = + mElementCollections.getFirst()) { + head->Destroy(); // Note: this removes 'head' from mElementCollections. + } + } + + LinkedList> mElementCollections; + nsPresContext* mPresContext; // weak (non-null from ctor to Disconnect) +}; + +/** + * Utility class for referencing the element that created a CSS animation or + * transition. It is non-owning (i.e. it uses a raw pointer) since it is only + * expected to be set by the owned animation while it actually being managed + * by the owning element. + * + * This class also abstracts the comparison of an element/pseudo-class pair + * for the sake of composite ordering since this logic is common to both CSS + * animations and transitions. + * + * (We call this OwningElementRef instead of just OwningElement so that we can + * call the getter on CSSAnimation/CSSTransition OwningElement() without + * clashing with this object's contructor.) + */ +class OwningElementRef final { + public: + OwningElementRef() = default; + + explicit OwningElementRef(const NonOwningAnimationTarget& aTarget) + : mTarget(aTarget) {} + + OwningElementRef(dom::Element& aElement, PseudoStyleType aPseudoType) + : mTarget(&aElement, aPseudoType) {} + + bool Equals(const OwningElementRef& aOther) const { + return mTarget == aOther.mTarget; + } + + bool LessThan(Maybe& aChildIndex, const OwningElementRef& aOther, + Maybe& aOtherChildIndex) const { + MOZ_ASSERT(mTarget.mElement && aOther.mTarget.mElement, + "Elements to compare should not be null"); + + if (mTarget.mElement != aOther.mTarget.mElement) { + return nsContentUtils::PositionIsBefore(mTarget.mElement, + aOther.mTarget.mElement, + &aChildIndex, &aOtherChildIndex); + } + + return mTarget.mPseudoType == PseudoStyleType::NotPseudo || + (mTarget.mPseudoType == PseudoStyleType::before && + aOther.mTarget.mPseudoType == PseudoStyleType::after) || + (mTarget.mPseudoType == PseudoStyleType::marker && + aOther.mTarget.mPseudoType == PseudoStyleType::before) || + (mTarget.mPseudoType == PseudoStyleType::marker && + aOther.mTarget.mPseudoType == PseudoStyleType::after); + } + + bool IsSet() const { return !!mTarget.mElement; } + + void GetElement(dom::Element*& aElement, PseudoStyleType& aPseudoType) const { + aElement = mTarget.mElement; + aPseudoType = mTarget.mPseudoType; + } + + const NonOwningAnimationTarget& Target() const { return mTarget; } + + nsPresContext* GetPresContext() const { + return nsContentUtils::GetContextForContent(mTarget.mElement); + } + + private: + NonOwningAnimationTarget mTarget; +}; + +// Return the TransitionPhase or AnimationPhase to use when the animation +// doesn't have a target effect. +template +PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation) { + MOZ_ASSERT(!aAnimation.GetEffect(), + "Should only be called when we do not have an effect"); + + dom::Nullable currentTime = + aAnimation.GetCurrentTimeAsDuration(); + if (currentTime.IsNull()) { + return PhaseType::Idle; + } + + // If we don't have a target effect, the duration will be zero so the phase is + // 'before' if the current time is less than zero. + return currentTime.Value() < TimeDuration() ? PhaseType::Before + : PhaseType::After; +}; + +inline dom::PlaybackDirection StyleToDom(StyleAnimationDirection aDirection) { + switch (aDirection) { + case StyleAnimationDirection::Normal: + return dom::PlaybackDirection::Normal; + case StyleAnimationDirection::Reverse: + return dom::PlaybackDirection::Reverse; + case StyleAnimationDirection::Alternate: + return dom::PlaybackDirection::Alternate; + case StyleAnimationDirection::AlternateReverse: + return dom::PlaybackDirection::Alternate_reverse; + } + MOZ_ASSERT_UNREACHABLE("Wrong style value?"); + return dom::PlaybackDirection::Normal; +} + +inline dom::FillMode StyleToDom(StyleAnimationFillMode aFillMode) { + switch (aFillMode) { + case StyleAnimationFillMode::None: + return dom::FillMode::None; + case StyleAnimationFillMode::Both: + return dom::FillMode::Both; + case StyleAnimationFillMode::Forwards: + return dom::FillMode::Forwards; + case StyleAnimationFillMode::Backwards: + return dom::FillMode::Backwards; + } + MOZ_ASSERT_UNREACHABLE("Wrong style value?"); + return dom::FillMode::None; +} + +inline dom::CompositeOperation StyleToDom(StyleAnimationComposition aStyle) { + switch (aStyle) { + case StyleAnimationComposition::Replace: + return dom::CompositeOperation::Replace; + case StyleAnimationComposition::Add: + return dom::CompositeOperation::Add; + case StyleAnimationComposition::Accumulate: + return dom::CompositeOperation::Accumulate; + } + MOZ_ASSERT_UNREACHABLE("Invalid style composite operation?"); + return dom::CompositeOperation::Replace; +} + +inline TimingParams TimingParamsFromCSSParams( + float aDuration, float aDelay, float aIterationCount, + StyleAnimationDirection aDirection, StyleAnimationFillMode aFillMode) { + MOZ_ASSERT(aIterationCount >= 0.0 && !std::isnan(aIterationCount), + "aIterations should be nonnegative & finite, as ensured by " + "CSSParser"); + return TimingParams{aDuration, aDelay, aIterationCount, + StyleToDom(aDirection), StyleToDom(aFillMode)}; +} + +} // namespace mozilla + +#endif /* !defined(mozilla_css_AnimationCommon_h) */ diff --git a/layout/style/AttributeStyles.cpp b/layout/style/AttributeStyles.cpp new file mode 100644 index 0000000000..5bf74d2697 --- /dev/null +++ b/layout/style/AttributeStyles.cpp @@ -0,0 +1,112 @@ +/* -*- 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/. */ + +/* Data from presentational HTML attributes and style attributes */ + +#include "mozilla/AttributeStyles.h" + +#include "nsGkAtoms.h" +#include "nsPresContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Element.h" +#include "nsStyleConsts.h" +#include "nsError.h" +#include "nsHashKeys.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/OperatorNewExtensions.h" +#include "mozilla/PresShell.h" +#include "mozilla/RestyleManager.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoStyleSet.h" + +using namespace mozilla::dom; + +namespace mozilla { + +// ----------------------------------------------------------- + +AttributeStyles::AttributeStyles(Document* aDocument) : mDocument(aDocument) { + MOZ_ASSERT(aDocument); +} + +void AttributeStyles::SetOwningDocument(Document* aDocument) { + mDocument = aDocument; // not refcounted +} + +void AttributeStyles::Reset() { + mServoUnvisitedLinkDecl = nullptr; + mServoVisitedLinkDecl = nullptr; + mServoActiveLinkDecl = nullptr; +} + +nsresult AttributeStyles::ImplLinkColorSetter( + RefPtr& aDecl, nscolor aColor) { + if (!mDocument || !mDocument->GetPresShell()) { + return NS_OK; + } + + MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal()); + aDecl = Servo_DeclarationBlock_CreateEmpty().Consume(); + Servo_DeclarationBlock_SetColorValue(aDecl.get(), eCSSProperty_color, aColor); + + // Now make sure we restyle any links that might need it. This + // shouldn't happen often, so just rebuilding everything is ok. + if (Element* root = mDocument->GetRootElement()) { + RestyleManager* rm = mDocument->GetPresContext()->RestyleManager(); + rm->PostRestyleEvent(root, RestyleHint::RestyleSubtree(), nsChangeHint(0)); + } + return NS_OK; +} + +nsresult AttributeStyles::SetLinkColor(nscolor aColor) { + return ImplLinkColorSetter(mServoUnvisitedLinkDecl, aColor); +} + +nsresult AttributeStyles::SetActiveLinkColor(nscolor aColor) { + return ImplLinkColorSetter(mServoActiveLinkDecl, aColor); +} + +nsresult AttributeStyles::SetVisitedLinkColor(nscolor aColor) { + return ImplLinkColorSetter(mServoVisitedLinkDecl, aColor); +} + +size_t AttributeStyles::DOMSizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mServoUnvisitedLinkDecl; + // - mServoVisitedLinkDecl; + // - mServoActiveLinkDecl; + // + // The following members are not measured: + // - mDocument, because it's non-owning + return n; +} + +AttributeStyles::~AttributeStyles() { + // We may go away before all of our cached style attributes do, so clean up + // any that are left. + for (auto iter = mCachedStyleAttrs.Iter(); !iter.Done(); iter.Next()) { + MiscContainer*& value = iter.Data(); + + // Ideally we'd just call MiscContainer::Evict, but we can't do that since + // we're iterating the hashtable. + if (value->mType == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* declaration = value->mValue.mCSSDeclaration; + declaration->SetAttributeStyles(nullptr); + } else { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); + } + + value->mValue.mCached = 0; + iter.Remove(); + } +} + +} // namespace mozilla diff --git a/layout/style/AttributeStyles.h b/layout/style/AttributeStyles.h new file mode 100644 index 0000000000..82220cdb56 --- /dev/null +++ b/layout/style/AttributeStyles.h @@ -0,0 +1,86 @@ +/* -*- 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/. */ + +/* + * style sheet and style rule processor representing data from presentational + * HTML attributes + */ + +#ifndef mozilla_AttributeStyles_h_ +#define mozilla_AttributeStyles_h_ + +#include "nsColor.h" +#include "nsCOMPtr.h" +#include "nsAtom.h" +#include "PLDHashTable.h" +#include "mozilla/Attributes.h" +#include "mozilla/MemoryReporting.h" +#include "nsTHashMap.h" +#include "nsString.h" + +struct MiscContainer; +namespace mozilla { +struct StyleLockedDeclarationBlock; +namespace dom { +class Document; +} // namespace dom + +class AttributeStyles final { + public: + explicit AttributeStyles(dom::Document* aDocument); + void SetOwningDocument(dom::Document* aDocument); + + NS_INLINE_DECL_REFCOUNTING(AttributeStyles) + + size_t DOMSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + void Reset(); + nsresult SetLinkColor(nscolor aColor); + nsresult SetActiveLinkColor(nscolor aColor); + nsresult SetVisitedLinkColor(nscolor aColor); + + const StyleLockedDeclarationBlock* GetServoUnvisitedLinkDecl() const { + return mServoUnvisitedLinkDecl; + } + const StyleLockedDeclarationBlock* GetServoVisitedLinkDecl() const { + return mServoVisitedLinkDecl; + } + const StyleLockedDeclarationBlock* GetServoActiveLinkDecl() const { + return mServoActiveLinkDecl; + } + + void CacheStyleAttr(const nsAString& aSerialized, MiscContainer* aValue) { + mCachedStyleAttrs.InsertOrUpdate(aSerialized, aValue); + } + void EvictStyleAttr(const nsAString& aSerialized, MiscContainer* aValue) { + NS_ASSERTION(LookupStyleAttr(aSerialized) == aValue, + "Cached value doesn't match?"); + mCachedStyleAttrs.Remove(aSerialized); + } + MiscContainer* LookupStyleAttr(const nsAString& aSerialized) { + return mCachedStyleAttrs.Get(aSerialized); + } + + AttributeStyles(const AttributeStyles& aCopy) = delete; + AttributeStyles& operator=(const AttributeStyles& aCopy) = delete; + + private: + ~AttributeStyles(); + + // Implementation of SetLink/VisitedLink/ActiveLinkColor + nsresult ImplLinkColorSetter(RefPtr& aDecl, + nscolor aColor); + + dom::Document* mDocument; + RefPtr mServoUnvisitedLinkDecl; + RefPtr mServoVisitedLinkDecl; + RefPtr mServoActiveLinkDecl; + nsTHashMap mCachedStyleAttrs; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/BuiltinCounterStyleList.h b/layout/style/BuiltinCounterStyleList.h new file mode 100644 index 0000000000..e68b72e59d --- /dev/null +++ b/layout/style/BuiltinCounterStyleList.h @@ -0,0 +1,38 @@ +/* -*- 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 list of all builtin counter styles */ + +/* Each entry is defined as a BUILTIN_COUNTER_STYLE macro with the + * following parameters: + * - 'value_' which is final part of name of NS_STYLE_LIST_STYLE_* macros + * - 'atom_' which is the corresponding atom in nsGkAtoms table + * + * Users of this list should define the following macro before including + * this file: BUILTIN_COUNTER_STYLE(value_, atom_) + */ + +// none and decimal are not redefinable, so they have to be builtin. +BUILTIN_COUNTER_STYLE(None, none) +BUILTIN_COUNTER_STYLE(Decimal, decimal) +// the following graphic styles are processed in a different way. +BUILTIN_COUNTER_STYLE(Disc, disc) +BUILTIN_COUNTER_STYLE(Circle, circle) +BUILTIN_COUNTER_STYLE(Square, square) +BUILTIN_COUNTER_STYLE(DisclosureClosed, disclosure_closed) +BUILTIN_COUNTER_STYLE(DisclosureOpen, disclosure_open) +// the following counter styles require specific algorithms to generate. +BUILTIN_COUNTER_STYLE(Hebrew, hebrew) +BUILTIN_COUNTER_STYLE(JapaneseInformal, japanese_informal) +BUILTIN_COUNTER_STYLE(JapaneseFormal, japanese_formal) +BUILTIN_COUNTER_STYLE(KoreanHangulFormal, korean_hangul_formal) +BUILTIN_COUNTER_STYLE(KoreanHanjaInformal, korean_hanja_informal) +BUILTIN_COUNTER_STYLE(KoreanHanjaFormal, korean_hanja_formal) +BUILTIN_COUNTER_STYLE(SimpChineseInformal, simp_chinese_informal) +BUILTIN_COUNTER_STYLE(SimpChineseFormal, simp_chinese_formal) +BUILTIN_COUNTER_STYLE(TradChineseInformal, trad_chinese_informal) +BUILTIN_COUNTER_STYLE(TradChineseFormal, trad_chinese_formal) +BUILTIN_COUNTER_STYLE(EthiopicNumeric, ethiopic_numeric) diff --git a/layout/style/CSS.cpp b/layout/style/CSS.cpp new file mode 100644 index 0000000000..28db5a34c5 --- /dev/null +++ b/layout/style/CSS.cpp @@ -0,0 +1,71 @@ +/* -*- 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/. */ + +/* DOM object holding utility CSS functions */ + +#include "CSS.h" + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/HighlightRegistry.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "nsStyleUtil.h" +#include "xpcpublic.h" + +namespace mozilla::dom { + +/* static */ +bool CSS::Supports(const GlobalObject&, const nsACString& aProperty, + const nsACString& aValue) { + return Servo_CSSSupports2(&aProperty, &aValue); +} + +/* static */ +bool CSS::Supports(const GlobalObject&, const nsACString& aCondition) { + return Servo_CSSSupports(&aCondition, /* ua = */ false, /* chrome = */ false, + /* quirks = */ false); +} + +/* static */ +void CSS::Escape(const GlobalObject&, const nsAString& aIdent, + nsAString& aReturn) { + nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn); +} + +static Document* GetDocument(const GlobalObject& aGlobal) { + nsCOMPtr window = + do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_DIAGNOSTIC_ASSERT(window, "CSS is only exposed to window globals"); + if (!window) { + return nullptr; + } + return window->GetExtantDoc(); +} + +/* static */ +HighlightRegistry* CSS::GetHighlights(const GlobalObject& aGlobal, + ErrorResult& aRv) { + Document* doc = GetDocument(aGlobal); + if (!doc) { + aRv.ThrowUnknownError("No document associated to this global?"); + return nullptr; + } + return &doc->HighlightRegistry(); +} + +/* static */ +void CSS::RegisterProperty(const GlobalObject& aGlobal, + const PropertyDefinition& aDefinition, + ErrorResult& aRv) { + Document* doc = GetDocument(aGlobal); + if (!doc) { + return aRv.ThrowUnknownError("No document associated to this global?"); + } + doc->EnsureStyleSet().RegisterProperty(aDefinition, aRv); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSS.h b/layout/style/CSS.h new file mode 100644 index 0000000000..fc3d6ff96f --- /dev/null +++ b/layout/style/CSS.h @@ -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/. */ + +/* DOM object holding utility CSS functions */ + +#ifndef mozilla_dom_CSS_h_ +#define mozilla_dom_CSS_h_ + +#include "mozilla/Attributes.h" +#include "nsStringFwd.h" + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; +class HighlightRegistry; +struct PropertyDefinition; + +class CSS { + public: + CSS() = delete; + + static bool Supports(const GlobalObject&, const nsACString& aProperty, + const nsACString& aValue); + + static bool Supports(const GlobalObject&, const nsACString& aDeclaration); + + static void Escape(const GlobalObject&, const nsAString& aIdent, + nsAString& aReturn); + + static HighlightRegistry* GetHighlights(const GlobalObject& aGlobal, + ErrorResult& aRv); + + static void RegisterProperty(const GlobalObject&, const PropertyDefinition&, + ErrorResult&); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CSS_h_ diff --git a/layout/style/CSSContainerRule.cpp b/layout/style/CSSContainerRule.cpp new file mode 100644 index 0000000000..4d798c0dda --- /dev/null +++ b/layout/style/CSSContainerRule.cpp @@ -0,0 +1,91 @@ +/* -*- 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 "mozilla/dom/CSSContainerRule.h" + +#include "mozilla/css/GroupRule.h" +#include "mozilla/dom/CSSContainerRuleBinding.h" +#include "mozilla/ServoBindings.h" + +using namespace mozilla::css; + +namespace mozilla::dom { + +CSSContainerRule::CSSContainerRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::ConditionRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +CSSContainerRule::~CSSContainerRule() = default; + +NS_IMPL_ADDREF_INHERITED(CSSContainerRule, ConditionRule) +NS_IMPL_RELEASE_INHERITED(CSSContainerRule, ConditionRule) + +// QueryInterface implementation for ContainerRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSContainerRule) +NS_INTERFACE_MAP_END_INHERITING(ConditionRule) + +#ifdef DEBUG +/* virtual */ +void CSSContainerRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_ContainerRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +already_AddRefed CSSContainerRule::GetOrCreateRawRules() { + return Servo_ContainerRule_GetRules(mRawRule).Consume(); +} + +StyleCssRuleType CSSContainerRule::Type() const { + return StyleCssRuleType::Container; +} + +void CSSContainerRule::GetConditionText(nsACString& aConditionText) { + Servo_ContainerRule_GetConditionText(mRawRule, &aConditionText); +} + +/* virtual */ +void CSSContainerRule::GetCssText(nsACString& aCssText) const { + Servo_ContainerRule_GetCssText(mRawRule, &aCssText); +} + +void CSSContainerRule::GetContainerName(nsACString& aName) const { + Servo_ContainerRule_GetContainerName(mRawRule, &aName); +} + +void CSSContainerRule::GetContainerQuery(nsACString& aQuery) const { + Servo_ContainerRule_GetContainerQuery(mRawRule, &aQuery); +} + +Element* CSSContainerRule::QueryContainerFor(const Element& aElement) const { + return const_cast( + Servo_ContainerRule_QueryContainerFor(mRawRule, &aElement)); +} + +void CSSContainerRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + css::ConditionRule::DidSetRawAfterClone(); +} + +/* virtual */ +size_t CSSContainerRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +/* virtual */ +JSObject* CSSContainerRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSContainerRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSContainerRule.h b/layout/style/CSSContainerRule.h new file mode 100644 index 0000000000..683a17604e --- /dev/null +++ b/layout/style/CSSContainerRule.h @@ -0,0 +1,55 @@ +/* -*- 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_dom_CSSContainerRule_h +#define mozilla_dom_CSSContainerRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSContainerRule final : public css::ConditionRule { + public: + CSSContainerRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleContainerRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + + already_AddRefed GetOrCreateRawRules() final; + + // WebIDL interface + StyleCssRuleType Type() const override; + // WebIDL interface + void GetCssText(nsACString& aCssText) const final; + void GetConditionText(nsACString& aConditionText) final; + + void GetContainerName(nsACString&) const; + void GetContainerQuery(nsACString&) const; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + Element* QueryContainerFor(const Element&) const; + + private: + virtual ~CSSContainerRule(); + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSContainerRule_h diff --git a/layout/style/CSSCounterStyleRule.cpp b/layout/style/CSSCounterStyleRule.cpp new file mode 100644 index 0000000000..2a3f5a0f82 --- /dev/null +++ b/layout/style/CSSCounterStyleRule.cpp @@ -0,0 +1,101 @@ +/* -*- 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 "mozilla/dom/CSSCounterStyleRule.h" + +#include "mozAutoDocUpdate.h" +#include "mozilla/dom/CSSCounterStyleRuleBinding.h" +#include "mozilla/ServoBindings.h" +#include "nsStyleUtil.h" + +namespace mozilla { +namespace dom { + +bool CSSCounterStyleRule::IsCCLeaf() const { return Rule::IsCCLeaf(); } + +#ifdef DEBUG +void CSSCounterStyleRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_CounterStyleRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSCounterStyleRule::Type() const { + return StyleCssRuleType::CounterStyle; +} + +void CSSCounterStyleRule::SetRawAfterClone( + RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +void CSSCounterStyleRule::GetCssText(nsACString& aCssText) const { + Servo_CounterStyleRule_GetCssText(mRawRule, &aCssText); +} + +void CSSCounterStyleRule::GetName(nsAString& aName) { + aName.Truncate(); + nsAtom* name = Servo_CounterStyleRule_GetName(mRawRule); + nsDependentAtomString nameStr(name); + nsStyleUtil::AppendEscapedCSSIdent(nameStr, aName); +} + +template +void CSSCounterStyleRule::ModifyRule(Func aCallback) { + if (IsReadOnly()) { + return; + } + + StyleSheet* sheet = GetStyleSheet(); + if (sheet) { + sheet->WillDirty(); + } + + if (aCallback() && sheet) { + sheet->RuleChanged(this, StyleRuleChangeKind::Generic); + } +} + +void CSSCounterStyleRule::SetName(const nsAString& aName) { + ModifyRule([&] { + NS_ConvertUTF16toUTF8 name(aName); + return Servo_CounterStyleRule_SetName(mRawRule, &name); + }); +} + +#define CSS_COUNTER_DESC(name_, method_) \ + void CSSCounterStyleRule::Get##method_(nsACString& aValue) { \ + MOZ_ASSERT(aValue.IsEmpty()); \ + Servo_CounterStyleRule_GetDescriptorCssText( \ + mRawRule, eCSSCounterDesc_##method_, &aValue); \ + } \ + void CSSCounterStyleRule::Set##method_(const nsACString& aValue) { \ + ModifyRule([&] { \ + return Servo_CounterStyleRule_SetDescriptor( \ + mRawRule, eCSSCounterDesc_##method_, &aValue); \ + }); \ + } +#include "nsCSSCounterDescList.h" +#undef CSS_COUNTER_DESC + +/* virtual */ +size_t CSSCounterStyleRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +/* virtual */ +JSObject* CSSCounterStyleRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSCounterStyleRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/CSSCounterStyleRule.h b/layout/style/CSSCounterStyleRule.h new file mode 100644 index 0000000000..fa8bb19c5e --- /dev/null +++ b/layout/style/CSSCounterStyleRule.h @@ -0,0 +1,63 @@ +/* -*- 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_CSSCounterStyleRule_h +#define mozilla_CSSCounterStyleRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +struct StyleLockedCounterStyleRule; + +namespace mozilla::dom { + +class CSSCounterStyleRule final : public css::Rule { + public: + CSSCounterStyleRule(already_AddRefed aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + + private: + CSSCounterStyleRule(const CSSCounterStyleRule& aCopy) = delete; + ~CSSCounterStyleRule() = default; + + template + void ModifyRule(Func); + + public: + bool IsCCLeaf() const final; + + const StyleLockedCounterStyleRule* Raw() const { return mRawRule.get(); } + void SetRawAfterClone(RefPtr); + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + // WebIDL interface + StyleCssRuleType Type() const override; + void GetCssText(nsACString& aCssText) const override; + void GetName(nsAString& aName); + void SetName(const nsAString& aName); +#define CSS_COUNTER_DESC(name_, method_) \ + void Get##method_(nsACString& aValue); \ + void Set##method_(const nsACString& aValue); +#include "nsCSSCounterDescList.h" +#undef CSS_COUNTER_DESC + + size_t SizeOfIncludingThis(MallocSizeOf) const final; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + private: + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_CSSCounterStyleRule_h diff --git a/layout/style/CSSEnabledState.h b/layout/style/CSSEnabledState.h new file mode 100644 index 0000000000..b8793d1643 --- /dev/null +++ b/layout/style/CSSEnabledState.h @@ -0,0 +1,38 @@ +/* -*- 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/. */ + +/* + * enum for whether a CSS feature (property, pseudo-class, etc.) is + * enabled in a specific context + */ + +#ifndef mozilla_CSSEnabledState_h +#define mozilla_CSSEnabledState_h + +#include "mozilla/TypedEnumBits.h" + +namespace mozilla { + +enum class CSSEnabledState { + // The default CSSEnabledState: only enable what's enabled for all + // content, given the current values of preferences. + ForAllContent = 0, + // Enable features available in UA sheets. + InUASheets = 0x01, + // Enable features available in chrome code. + InChrome = 0x02, + // Special value to unconditionally enable everything. This implies + // all the bits above, but is strictly more than just their OR-ed + // union. This just skips any test so a feature will be enabled even + // if it would have been disabled with all the bits above set. + IgnoreEnabledState = 0xff +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSEnabledState) + +} // namespace mozilla + +#endif // mozilla_CSSEnabledState_h diff --git a/layout/style/CSSFontFaceRule.cpp b/layout/style/CSSFontFaceRule.cpp new file mode 100644 index 0000000000..1115c9223f --- /dev/null +++ b/layout/style/CSSFontFaceRule.cpp @@ -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/. */ + +#include "mozilla/dom/CSSFontFaceRule.h" + +#include "mozilla/dom/CSSFontFaceRuleBinding.h" +#include "mozilla/dom/CSSStyleDeclarationBinding.h" +#include "mozilla/ServoBindings.h" +#include "nsCSSProps.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// ------------------------------------------- +// CSSFontFaceRuleDecl and related routines +// + +// QueryInterface implementation for CSSFontFaceRuleDecl +NS_INTERFACE_MAP_BEGIN(CSSFontFaceRuleDecl) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration) + NS_INTERFACE_MAP_ENTRY(nsISupports) + // We forward the cycle collection interfaces to ContainingRule(), which is + // never null (in fact, we're part of that object!) + if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || + aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { + return ContainingRule()->QueryInterface(aIID, aInstancePtr); + } else +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF_USING_AGGREGATOR(CSSFontFaceRuleDecl, ContainingRule()) +NS_IMPL_RELEASE_USING_AGGREGATOR(CSSFontFaceRuleDecl, ContainingRule()) + +void CSSFontFaceRuleDecl::SetRawAfterClone( + RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +// helper for string GetPropertyValue and RemovePropertyValue +void CSSFontFaceRuleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID, + nsACString& aResult) const { + MOZ_ASSERT(aResult.IsEmpty()); + Servo_FontFaceRule_GetDescriptorCssText(mRawRule, aFontDescID, &aResult); +} + +void CSSFontFaceRuleDecl::GetCssText(nsACString& aCssText) { + MOZ_ASSERT(aCssText.IsEmpty()); + Servo_FontFaceRule_GetDeclCssText(mRawRule, &aCssText); +} + +void CSSFontFaceRuleDecl::SetCssText(const nsACString& aCssText, + nsIPrincipal* aSubjectPrincipal, + ErrorResult& aRv) { + if (ContainingRule()->IsReadOnly()) { + return; + } + // bug 443978 + aRv.ThrowNotSupportedError( + "Can't set cssText on CSSFontFaceRule declarations"); +} + +void CSSFontFaceRuleDecl::GetPropertyValue(const nsACString& aPropName, + nsACString& aResult) { + aResult.Truncate(); + nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName); + if (descID != eCSSFontDesc_UNKNOWN) { + GetPropertyValue(descID, aResult); + } +} + +void CSSFontFaceRuleDecl::RemoveProperty(const nsACString& aPropName, + nsACString& aResult, + ErrorResult& aRv) { + nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName); + NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN && descID < eCSSFontDesc_COUNT, + "LookupFontDesc returned value out of range"); + + if (ContainingRule()->IsReadOnly()) { + return; + } + + aResult.Truncate(); + if (descID != eCSSFontDesc_UNKNOWN) { + GetPropertyValue(descID, aResult); + Servo_FontFaceRule_ResetDescriptor(mRawRule, descID); + } +} + +void CSSFontFaceRuleDecl::GetPropertyPriority(const nsACString& aPropName, + nsACString& aResult) { + // font descriptors do not have priorities at present + aResult.Truncate(); +} + +void CSSFontFaceRuleDecl::SetProperty(const nsACString& aPropName, + const nsACString& aValue, + const nsACString& aPriority, + nsIPrincipal* aSubjectPrincipal, + ErrorResult& aRv) { + // FIXME(heycam): If we are changing unicode-range, then a FontFace object + // representing this rule must have its mUnicodeRange value invalidated. + + if (ContainingRule()->IsReadOnly()) { + return; + } + + // bug 443978 + aRv.ThrowNotSupportedError( + "Can't set properties on CSSFontFaceRule declarations"); +} + +uint32_t CSSFontFaceRuleDecl::Length() { + return Servo_FontFaceRule_Length(mRawRule); +} + +void CSSFontFaceRuleDecl::IndexedGetter(uint32_t aIndex, bool& aFound, + nsACString& aResult) { + nsCSSFontDesc id = Servo_FontFaceRule_IndexGetter(mRawRule, aIndex); + if (id != eCSSFontDesc_UNKNOWN) { + aFound = true; + aResult.Assign(nsCSSProps::GetStringValue(id)); + } else { + aFound = false; + } +} + +css::Rule* CSSFontFaceRuleDecl::GetParentRule() { return ContainingRule(); } + +nsINode* CSSFontFaceRuleDecl::GetAssociatedNode() const { + return ContainingRule()->GetAssociatedDocumentOrShadowRoot(); +} + +nsISupports* CSSFontFaceRuleDecl::GetParentObject() const { + return ContainingRule()->GetParentObject(); +} + +JSObject* CSSFontFaceRuleDecl::WrapObject(JSContext* cx, + JS::Handle aGivenProto) { + // If this changes to use a different type, remove the 'concrete' + // annotation from CSSStyleDeclaration. + return CSSStyleDeclaration_Binding::Wrap(cx, this, aGivenProto); +} + +// ------------------------------------------- +// CSSFontFaceRule +// + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSFontFaceRule) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSFontFaceRule, + mozilla::css::Rule) + // Keep this in sync with IsCCLeaf. + + // Trace the wrapper for our declaration. This just expands out + // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use + // directly because the wrapper is on the declaration, not on us. + tmp->mDecl.TraceWrapper(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSFontFaceRule) + // Keep this in sync with IsCCLeaf. + + // Unlink the wrapper for our declaration. + // + // Note that this has to happen before unlinking css::Rule. + tmp->UnlinkDeclarationWrapper(tmp->mDecl); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(mozilla::css::Rule) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSFontFaceRule, + mozilla::css::Rule) + // Keep this in sync with IsCCLeaf. +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSFontFaceRule::IsCCLeaf() const { + if (!Rule::IsCCLeaf()) { + return false; + } + + return !mDecl.PreservingWrapper(); +} + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSFontFaceRule, + mozilla::css::Rule) + +#ifdef DEBUG +void CSSFontFaceRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_FontFaceRule_Debug(Raw(), &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSFontFaceRule::Type() const { + return StyleCssRuleType::FontFace; +} + +void CSSFontFaceRule::SetRawAfterClone(RefPtr aRaw) { + mDecl.SetRawAfterClone(std::move(aRaw)); +} + +void CSSFontFaceRule::GetCssText(nsACString& aCssText) const { + aCssText.Truncate(); + Servo_FontFaceRule_GetCssText(Raw(), &aCssText); +} + +nsICSSDeclaration* CSSFontFaceRule::Style() { return &mDecl; } + +/* virtual */ +size_t CSSFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +/* virtual */ +JSObject* CSSFontFaceRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSFontFaceRule_Binding::Wrap(aCx, this, aGivenProto); +} diff --git a/layout/style/CSSFontFaceRule.h b/layout/style/CSSFontFaceRule.h new file mode 100644 index 0000000000..b29249b8a3 --- /dev/null +++ b/layout/style/CSSFontFaceRule.h @@ -0,0 +1,104 @@ +/* -*- 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_CSSFontFaceRule_h +#define mozilla_CSSFontFaceRule_h + +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/css/Rule.h" +#include "nsICSSDeclaration.h" + +namespace mozilla::dom { + +// A CSSFontFaceRuleDecl is always embeded in a CSSFontFaceRule. +class CSSFontFaceRule; +class CSSFontFaceRuleDecl final : public nsICSSDeclaration { + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER + + nsINode* GetAssociatedNode() const final; + nsISupports* GetParentObject() const final; + void IndexedGetter(uint32_t aIndex, bool& aFound, + nsACString& aPropName) final; + + void GetPropertyValue(nsCSSFontDesc aFontDescID, nsACString& aResult) const; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + protected: + // For accessing the constructor. + friend class CSSFontFaceRule; + + explicit CSSFontFaceRuleDecl(already_AddRefed aDecl) + : mRawRule(std::move(aDecl)) {} + + ~CSSFontFaceRuleDecl() = default; + + inline CSSFontFaceRule* ContainingRule(); + inline const CSSFontFaceRule* ContainingRule() const; + + RefPtr mRawRule; + void SetRawAfterClone(RefPtr); + + private: + void* operator new(size_t size) noexcept(true) = delete; +}; + +class CSSFontFaceRule final : public css::Rule { + public: + CSSFontFaceRule(already_AddRefed aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mDecl(std::move(aRawRule)) {} + + CSSFontFaceRule(const CSSFontFaceRule&) = delete; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSFontFaceRule, + css::Rule) + bool IsCCLeaf() const final; + + StyleLockedFontFaceRule* Raw() const { return mDecl.mRawRule; } + void SetRawAfterClone(RefPtr); + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + nsICSSDeclaration* Style(); + + // Methods of mozilla::css::Rule + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + private: + virtual ~CSSFontFaceRule() = default; + + // For computing the offset of mDecl. + friend class CSSFontFaceRuleDecl; + + CSSFontFaceRuleDecl mDecl; +}; + +inline CSSFontFaceRule* CSSFontFaceRuleDecl::ContainingRule() { + return reinterpret_cast(reinterpret_cast(this) - + offsetof(CSSFontFaceRule, mDecl)); +} + +inline const CSSFontFaceRule* CSSFontFaceRuleDecl::ContainingRule() const { + return reinterpret_cast( + reinterpret_cast(this) - offsetof(CSSFontFaceRule, mDecl)); +} + +} // namespace mozilla::dom + +#endif // mozilla_CSSFontFaceRule_h diff --git a/layout/style/CSSFontFeatureValuesRule.cpp b/layout/style/CSSFontFeatureValuesRule.cpp new file mode 100644 index 0000000000..80e4a909f6 --- /dev/null +++ b/layout/style/CSSFontFeatureValuesRule.cpp @@ -0,0 +1,84 @@ +/* -*- 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 "mozilla/dom/CSSFontFeatureValuesRule.h" +#include "mozilla/dom/CSSFontFeatureValuesRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +size_t CSSFontFeatureValuesRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +StyleCssRuleType CSSFontFeatureValuesRule::Type() const { + return StyleCssRuleType::FontFeatureValues; +} + +#ifdef DEBUG +void CSSFontFeatureValuesRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_FontFeatureValuesRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +void CSSFontFeatureValuesRule::SetRawAfterClone( + RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +/* CSSRule implementation */ + +void CSSFontFeatureValuesRule::GetCssText(nsACString& aCssText) const { + Servo_FontFeatureValuesRule_GetCssText(mRawRule, &aCssText); +} + +/* CSSFontFeatureValuesRule implementation */ + +void CSSFontFeatureValuesRule::GetFontFamily(nsACString& aFamilyListStr) { + Servo_FontFeatureValuesRule_GetFontFamily(mRawRule, &aFamilyListStr); +} + +void CSSFontFeatureValuesRule::GetValueText(nsACString& aValueText) { + Servo_FontFeatureValuesRule_GetValueText(mRawRule, &aValueText); +} + +void CSSFontFeatureValuesRule::SetFontFamily(const nsACString& aFontFamily, + ErrorResult& aRv) { + if (IsReadOnly()) { + return; + } + + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); +} + +void CSSFontFeatureValuesRule::SetValueText(const nsACString& aValueText, + ErrorResult& aRv) { + if (IsReadOnly()) { + return; + } + + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); +} + +// If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf +// implementation. + +bool CSSFontFeatureValuesRule::IsCCLeaf() const { return Rule::IsCCLeaf(); } + +/* virtual */ +JSObject* CSSFontFeatureValuesRule::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return CSSFontFeatureValuesRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSFontFeatureValuesRule.h b/layout/style/CSSFontFeatureValuesRule.h new file mode 100644 index 0000000000..517d2ebd3a --- /dev/null +++ b/layout/style/CSSFontFeatureValuesRule.h @@ -0,0 +1,57 @@ +/* -*- 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_dom_CSSFontFeatureValuesRule_h +#define mozilla_dom_CSSFontFeatureValuesRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +#include "nsICSSDeclaration.h" + +namespace mozilla::dom { + +class CSSFontFeatureValuesRule final : public css::Rule { + public: + CSSFontFeatureValuesRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + + virtual bool IsCCLeaf() const override; + + StyleFontFeatureValuesRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr aRaw); + + // WebIDL interfaces + StyleCssRuleType Type() const final; + + void GetCssText(nsACString& aCssText) const override; + void GetFontFamily(nsACString& aFamily); + void SetFontFamily(const nsACString& aFamily, mozilla::ErrorResult& aRv); + void GetValueText(nsACString& aValueText); + void SetValueText(const nsACString& aValueText, mozilla::ErrorResult& aRv); + + size_t SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) const override; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + private: + ~CSSFontFeatureValuesRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSFontFeatureValuesRule_h diff --git a/layout/style/CSSFontPaletteValuesRule.cpp b/layout/style/CSSFontPaletteValuesRule.cpp new file mode 100644 index 0000000000..b795461eb6 --- /dev/null +++ b/layout/style/CSSFontPaletteValuesRule.cpp @@ -0,0 +1,74 @@ +/* -*- 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 "mozilla/dom/CSSFontPaletteValuesRule.h" +#include "mozilla/dom/CSSFontPaletteValuesRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +size_t CSSFontPaletteValuesRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +StyleCssRuleType CSSFontPaletteValuesRule::Type() const { + return StyleCssRuleType::FontPaletteValues; +} + +#ifdef DEBUG +void CSSFontPaletteValuesRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_FontPaletteValuesRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +void CSSFontPaletteValuesRule::SetRawAfterClone( + RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +/* CSSRule implementation */ + +void CSSFontPaletteValuesRule::GetCssText(nsACString& aCssText) const { + Servo_FontPaletteValuesRule_GetCssText(mRawRule, &aCssText); +} + +/* CSSFontPaletteValuesRule implementation */ + +void CSSFontPaletteValuesRule::GetName(nsACString& aNameStr) const { + Servo_FontPaletteValuesRule_GetName(mRawRule, &aNameStr); +} + +void CSSFontPaletteValuesRule::GetFontFamily(nsACString& aFamilyListStr) const { + Servo_FontPaletteValuesRule_GetFontFamily(mRawRule, &aFamilyListStr); +} + +void CSSFontPaletteValuesRule::GetBasePalette(nsACString& aPaletteStr) const { + Servo_FontPaletteValuesRule_GetBasePalette(mRawRule, &aPaletteStr); +} + +void CSSFontPaletteValuesRule::GetOverrideColors(nsACString& aColorsStr) const { + Servo_FontPaletteValuesRule_GetOverrideColors(mRawRule, &aColorsStr); +} + +// If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf +// implementation. + +bool CSSFontPaletteValuesRule::IsCCLeaf() const { return Rule::IsCCLeaf(); } + +/* virtual */ +JSObject* CSSFontPaletteValuesRule::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return CSSFontPaletteValuesRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSFontPaletteValuesRule.h b/layout/style/CSSFontPaletteValuesRule.h new file mode 100644 index 0000000000..9020a2b35f --- /dev/null +++ b/layout/style/CSSFontPaletteValuesRule.h @@ -0,0 +1,59 @@ +/* -*- 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_dom_CSSFontPaletteValuesRule_h +#define mozilla_dom_CSSFontPaletteValuesRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +#include "nsICSSDeclaration.h" + +namespace mozilla::dom { + +class CSSFontPaletteValuesRule final : public css::Rule { + public: + CSSFontPaletteValuesRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + + bool IsCCLeaf() const final; + + StyleFontPaletteValuesRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr aRaw); + + // WebIDL interfaces + StyleCssRuleType Type() const final; + + void GetCssText(nsACString& aCssText) const final; + + void GetFontFamily(nsACString& aFamily) const; + + void GetName(nsACString& aName) const; + + void GetBasePalette(nsACString& aPalette) const; + + void GetOverrideColors(nsACString& aColors) const; + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + private: + ~CSSFontPaletteValuesRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSFontPaletteValuesRule_h diff --git a/layout/style/CSSImportRule.cpp b/layout/style/CSSImportRule.cpp new file mode 100644 index 0000000000..6ff6b4d44e --- /dev/null +++ b/layout/style/CSSImportRule.cpp @@ -0,0 +1,156 @@ +/* -*- 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 "mozilla/dom/CSSImportRule.h" + +#include "mozilla/dom/CSSImportRuleBinding.h" +#include "mozilla/dom/MediaList.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/StyleSheet.h" + +namespace mozilla { +namespace dom { + +CSSImportRule::CSSImportRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) { + const auto* sheet = Servo_ImportRule_GetSheet(mRawRule.get()); + mChildSheet = const_cast(sheet); + if (mChildSheet) { + mChildSheet->AddReferencingRule(*this); + } +} + +CSSImportRule::~CSSImportRule() { + if (mChildSheet) { + mChildSheet->RemoveReferencingRule(*this); + } +} + +// QueryInterface implementation for CSSImportRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSImportRule) +NS_INTERFACE_MAP_END_INHERITING(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSImportRule) + +NS_IMPL_ADDREF_INHERITED(CSSImportRule, css::Rule) +NS_IMPL_RELEASE_INHERITED(CSSImportRule, css::Rule) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSImportRule, css::Rule) + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildSheet"); + cb.NoteXPCOMChild(tmp->mChildSheet); + MOZ_ASSERT_IF(tmp->mRawRule, + Servo_ImportRule_GetSheet(tmp->mRawRule) == tmp->mChildSheet); + // Note the child sheet twice, since the Servo rule also holds a strong + // reference to it, but only if we're the "primary" rule reference. + if (tmp->mChildSheet && tmp->mChildSheet->GetOwnerRule() == tmp) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRawRule.stylesheet"); + cb.NoteXPCOMChild(tmp->mChildSheet); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSImportRule) + if (tmp->mChildSheet) { + tmp->mChildSheet->RemoveReferencingRule(*tmp); + tmp->mChildSheet = nullptr; + } + tmp->mRawRule = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::Rule) + +#ifdef DEBUG +/* virtual */ +void CSSImportRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_ImportRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSImportRule::Type() const { + return StyleCssRuleType::Import; +} + +void CSSImportRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + if (mChildSheet) { + mChildSheet->RemoveReferencingRule(*this); + } + mChildSheet = + const_cast(Servo_ImportRule_GetSheet(mRawRule.get())); + if (mChildSheet) { + mChildSheet->AddReferencingRule(*this); + } +} + +StyleSheet* CSSImportRule::GetStyleSheetForBindings() { + if (mChildSheet) { + // FIXME(emilio): This is needed to make sure we don't expose shared sheets + // to the OM (see wpt /css/cssom/cssimportrule-sheet-identity.html for + // example). + // + // Perhaps instead we could create a clone of the stylesheet and keep it in + // mChildSheet, without calling EnsureUniqueInner(), or something like that? + if (StyleSheet* parent = GetParentStyleSheet()) { + parent->EnsureUniqueInner(); + } + } + return mChildSheet; +} + +dom::MediaList* CSSImportRule::GetMedia() { + auto* sheet = GetStyleSheetForBindings(); + return sheet ? sheet->Media() : nullptr; +} + +void CSSImportRule::DropSheetReference() { + if (mChildSheet) { + mChildSheet->RemoveFromParent(); + } + Rule::DropSheetReference(); +} + +void CSSImportRule::GetHref(nsAString& aHref) const { + Servo_ImportRule_GetHref(mRawRule, &aHref); +} + +void CSSImportRule::GetLayerName(nsACString& aName) const { + Servo_ImportRule_GetLayerName(mRawRule, &aName); +} + +void CSSImportRule::GetSupportsText(nsACString& aSupportsText) const { + Servo_ImportRule_GetSupportsText(mRawRule, &aSupportsText); +} + +/* virtual */ +void CSSImportRule::GetCssText(nsACString& aCssText) const { + Servo_ImportRule_GetCssText(mRawRule, &aCssText); +} + +/* virtual */ +size_t CSSImportRule::SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +bool CSSImportRule::IsCCLeaf() const { + // We're not a leaf. + return false; +} + +/* virtual */ +JSObject* CSSImportRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSImportRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/CSSImportRule.h b/layout/style/CSSImportRule.h new file mode 100644 index 0000000000..fbe21a576a --- /dev/null +++ b/layout/style/CSSImportRule.h @@ -0,0 +1,63 @@ +/* -*- 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_dom_CSSImportRule_h +#define mozilla_dom_CSSImportRule_h + +#include "mozilla/css/Rule.h" + +namespace mozilla { + +class StyleSheet; + +namespace dom { + +class CSSImportRule final : public css::Rule { + public: + CSSImportRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSImportRule, css::Rule) + + bool IsCCLeaf() const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const override; + void GetHref(nsAString& aHref) const; + dom::MediaList* GetMedia(); + StyleSheet* GetStyleSheet() const { return mChildSheet; } + StyleSheet* GetStyleSheetForBindings(); + void GetLayerName(nsACString&) const; + void GetSupportsText(nsACString&) const; + + // Clear the mSheet pointer on this rule and descendants. + void DropSheetReference() final; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + const StyleLockedImportRule* Raw() const { return mRawRule.get(); } + void SetRawAfterClone(RefPtr); + + private: + ~CSSImportRule(); + + RefPtr mRawRule; + RefPtr mChildSheet; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CSSImportRule_h diff --git a/layout/style/CSSKeyframeRule.cpp b/layout/style/CSSKeyframeRule.cpp new file mode 100644 index 0000000000..e6ae2bada0 --- /dev/null +++ b/layout/style/CSSKeyframeRule.cpp @@ -0,0 +1,220 @@ +/* -*- 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 "mozilla/dom/CSSKeyframeRule.h" + +#include "mozilla/DeclarationBlock.h" +#include "mozilla/dom/CSSKeyframeRuleBinding.h" +#include "nsDOMCSSDeclaration.h" + +namespace mozilla::dom { + +// ------------------------------------------- +// CSSKeyframeDeclaration +// + +class CSSKeyframeDeclaration : public nsDOMCSSDeclaration { + public: + explicit CSSKeyframeDeclaration(CSSKeyframeRule* aRule) : mRule(aRule) { + mDecls = + new DeclarationBlock(Servo_Keyframe_GetStyle(aRule->Raw()).Consume()); + mDecls->SetOwningRule(aRule); + } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(CSSKeyframeDeclaration, + nsICSSDeclaration) + + css::Rule* GetParentRule() final { return mRule; } + + void DropReference() { + mRule = nullptr; + mDecls->SetOwningRule(nullptr); + } + + DeclarationBlock* GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) final { + if (aOperation != Operation::Read && mRule) { + if (StyleSheet* sheet = mRule->GetStyleSheet()) { + sheet->WillDirty(); + } + } + return mDecls; + } + nsresult SetCSSDeclaration(DeclarationBlock* aDecls, + MutationClosureData* aClosureData) final { + if (!mRule) { + return NS_OK; + } + mRule->UpdateRule([this, aDecls]() { + if (mDecls != aDecls) { + mDecls->SetOwningRule(nullptr); + mDecls = aDecls; + mDecls->SetOwningRule(mRule); + Servo_Keyframe_SetStyle(mRule->Raw(), mDecls->Raw()); + } + }); + return NS_OK; + } + ParsingEnvironment GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const final { + return GetParsingEnvironmentForRule(mRule, StyleCssRuleType::Keyframe); + } + Document* DocToUpdate() final { return nullptr; } + + nsINode* GetAssociatedNode() const final { + return mRule ? mRule->GetAssociatedDocumentOrShadowRoot() : nullptr; + } + + nsISupports* GetParentObject() const final { + return mRule ? mRule->GetParentObject() : nullptr; + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + // TODO we may want to add size of mDecls as well + return n; + } + + void SetRawAfterClone(StyleLockedKeyframe* aKeyframe) { + mDecls->SetOwningRule(nullptr); + mDecls = new DeclarationBlock(Servo_Keyframe_GetStyle(aKeyframe).Consume()); + mDecls->SetOwningRule(mRule); + } + + private: + virtual ~CSSKeyframeDeclaration() { + MOZ_ASSERT(!mRule, "Backpointer should have been cleared"); + } + + CSSKeyframeRule* mRule; + RefPtr mDecls; +}; + +NS_IMPL_CYCLE_COLLECTING_ADDREF(CSSKeyframeDeclaration) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CSSKeyframeDeclaration) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(CSSKeyframeDeclaration) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframeDeclaration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY +NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) + +// ------------------------------------------- +// CSSKeyframeRule +// + +CSSKeyframeRule::CSSKeyframeRule(already_AddRefed aRaw, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), mRaw(aRaw) {} + +CSSKeyframeRule::~CSSKeyframeRule() { + if (mDeclaration) { + mDeclaration->DropReference(); + } +} + +NS_IMPL_ADDREF_INHERITED(CSSKeyframeRule, css::Rule) +NS_IMPL_RELEASE_INHERITED(CSSKeyframeRule, css::Rule) + +// QueryInterface implementation for nsCSSKeyframeRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframeRule) +NS_INTERFACE_MAP_END_INHERITING(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSKeyframeRule) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CSSKeyframeRule, css::Rule) + if (tmp->mDeclaration) { + tmp->mDeclaration->DropReference(); + tmp->mDeclaration = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSKeyframeRule, css::Rule) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeclaration) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSKeyframeRule::IsCCLeaf() const { + return Rule::IsCCLeaf() && !mDeclaration; +} + +StyleCssRuleType CSSKeyframeRule::Type() const { + return StyleCssRuleType::Keyframe; +} + +void CSSKeyframeRule::SetRawAfterClone(RefPtr aRaw) { + mRaw = std::move(aRaw); + + if (mDeclaration) { + mDeclaration->SetRawAfterClone(mRaw); + } +} + +#ifdef DEBUG +/* virtual */ +void CSSKeyframeRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_Keyframe_Debug(mRaw, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +template +void CSSKeyframeRule::UpdateRule(Func aCallback) { + if (IsReadOnly()) { + return; + } + + StyleSheet* sheet = GetStyleSheet(); + if (sheet) { + sheet->WillDirty(); + } + + aCallback(); + + if (sheet) { + sheet->RuleChanged(this, StyleRuleChangeKind::Generic); + } +} + +void CSSKeyframeRule::GetKeyText(nsACString& aKeyText) { + Servo_Keyframe_GetKeyText(mRaw, &aKeyText); +} + +void CSSKeyframeRule::SetKeyText(const nsACString& aKeyText) { + UpdateRule( + [this, &aKeyText]() { Servo_Keyframe_SetKeyText(mRaw, &aKeyText); }); +} + +void CSSKeyframeRule::GetCssText(nsACString& aCssText) const { + Servo_Keyframe_GetCssText(mRaw, &aCssText); +} + +nsICSSDeclaration* CSSKeyframeRule::Style() { + if (!mDeclaration) { + mDeclaration = new CSSKeyframeDeclaration(this); + } + return mDeclaration; +} + +size_t CSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + if (mDeclaration) { + n += mDeclaration->SizeOfIncludingThis(aMallocSizeOf); + } + return n; +} + +/* virtual */ +JSObject* CSSKeyframeRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSKeyframeRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSKeyframeRule.h b/layout/style/CSSKeyframeRule.h new file mode 100644 index 0000000000..b1a202ced1 --- /dev/null +++ b/layout/style/CSSKeyframeRule.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_dom_CSSKeyframeRule_h +#define mozilla_dom_CSSKeyframeRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +class nsICSSDeclaration; + +namespace mozilla::dom { + +class CSSKeyframeDeclaration; + +class CSSKeyframeRule final : public css::Rule { + public: + CSSKeyframeRule(already_AddRefed aRaw, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSKeyframeRule, css::Rule) + bool IsCCLeaf() const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleLockedKeyframe* Raw() const { return mRaw; } + void SetRawAfterClone(RefPtr); + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + void GetKeyText(nsACString& aKey); + void SetKeyText(const nsACString& aKey); + nsICSSDeclaration* Style(); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + private: + virtual ~CSSKeyframeRule(); + + friend class CSSKeyframeDeclaration; + + template + void UpdateRule(Func aCallback); + + RefPtr mRaw; + // lazily created when needed + RefPtr mDeclaration; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSKeyframeRule_h diff --git a/layout/style/CSSKeyframesRule.cpp b/layout/style/CSSKeyframesRule.cpp new file mode 100644 index 0000000000..89fe1da32f --- /dev/null +++ b/layout/style/CSSKeyframesRule.cpp @@ -0,0 +1,364 @@ +/* -*- 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 "mozilla/dom/CSSKeyframesRule.h" + +#include "mozilla/dom/CSSKeyframesRuleBinding.h" +#include "mozilla/dom/CSSRuleList.h" +#include "mozilla/ServoBindings.h" +#include "nsCOMArray.h" + +#include + +namespace mozilla::dom { + +// ------------------------------------------- +// CSSKeyframeList +// + +class CSSKeyframeList : public dom::CSSRuleList { + public: + CSSKeyframeList(already_AddRefed aRawRule, + StyleSheet* aSheet, CSSKeyframesRule* aParentRule) + : mStyleSheet(aSheet), mParentRule(aParentRule), mRawRule(aRawRule) { + mRules.SetCount(Servo_KeyframesRule_GetCount(mRawRule)); + } + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSKeyframeList, dom::CSSRuleList) + + void SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + uint32_t index = 0; + for (css::Rule* rule : mRules) { + if (rule) { + uint32_t line = 0, column = 0; + RefPtr keyframe = + Servo_KeyframesRule_GetKeyframeAt(mRawRule, index, &line, &column) + .Consume(); + static_cast(rule)->SetRawAfterClone( + std::move(keyframe)); + } + index++; + } + } + + void DropSheetReference() { + if (!mStyleSheet) { + return; + } + mStyleSheet = nullptr; + for (css::Rule* rule : mRules) { + if (rule) { + rule->DropSheetReference(); + } + } + } + + StyleSheet* GetParentObject() final { return mStyleSheet; } + + CSSKeyframeRule* GetRule(uint32_t aIndex) { + if (!mRules[aIndex]) { + uint32_t line = 0, column = 0; + RefPtr rule = + Servo_KeyframesRule_GetKeyframeAt(mRawRule, aIndex, &line, &column) + .Consume(); + CSSKeyframeRule* ruleObj = new CSSKeyframeRule(rule.forget(), mStyleSheet, + mParentRule, line, column); + mRules.ReplaceObjectAt(ruleObj, aIndex); + } + return static_cast(mRules[aIndex]); + } + + CSSKeyframeRule* IndexedGetter(uint32_t aIndex, bool& aFound) final { + if (aIndex >= mRules.Length()) { + aFound = false; + return nullptr; + } + aFound = true; + return GetRule(aIndex); + } + + void AppendRule() { + MOZ_ASSERT(!mParentRule->IsReadOnly()); + mRules.AppendObject(nullptr); + } + + void RemoveRule(uint32_t aIndex) { + MOZ_ASSERT(!mParentRule->IsReadOnly()); + + if (aIndex >= mRules.Length()) { + return; + } + if (css::Rule* child = mRules[aIndex]) { + child->DropReferences(); + } + mRules.RemoveObjectAt(aIndex); + } + + uint32_t Length() final { return mRules.Length(); } + + void DropReferences() { + if (!mStyleSheet && !mParentRule) { + return; + } + mStyleSheet = nullptr; + mParentRule = nullptr; + for (css::Rule* rule : mRules) { + if (rule) { + rule->DropParentRuleReference(); + rule->DropSheetReference(); + } + } + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + for (const css::Rule* rule : mRules) { + n += rule ? rule->SizeOfIncludingThis(aMallocSizeOf) : 0; + } + return n; + } + + private: + virtual ~CSSKeyframeList() { + MOZ_ASSERT(!mParentRule, "Backpointer should have been cleared"); + MOZ_ASSERT(!mStyleSheet, "Backpointer should have been cleared"); + DropAllRules(); + } + + void DropAllRules() { + DropReferences(); + mRules.Clear(); + mRawRule = nullptr; + } + + // may be nullptr when the style sheet drops the reference to us. + StyleSheet* mStyleSheet = nullptr; + CSSKeyframesRule* mParentRule = nullptr; + RefPtr mRawRule; + nsCOMArray mRules; +}; + +// QueryInterface implementation for CSSKeyframeList +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframeList) +NS_INTERFACE_MAP_END_INHERITING(dom::CSSRuleList) + +NS_IMPL_ADDREF_INHERITED(CSSKeyframeList, dom::CSSRuleList) +NS_IMPL_RELEASE_INHERITED(CSSKeyframeList, dom::CSSRuleList) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSKeyframeList) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSKeyframeList) + tmp->DropAllRules(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSKeyframeList, + dom::CSSRuleList) + for (css::Rule* rule : tmp->mRules) { + if (rule) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]"); + cb.NoteXPCOMChild(rule); + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +// ------------------------------------------- +// CSSKeyframesRule +// + +CSSKeyframesRule::CSSKeyframesRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +CSSKeyframesRule::~CSSKeyframesRule() { + if (mKeyframeList) { + mKeyframeList->DropReferences(); + } +} + +NS_IMPL_ADDREF_INHERITED(CSSKeyframesRule, css::Rule) +NS_IMPL_RELEASE_INHERITED(CSSKeyframesRule, css::Rule) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSKeyframesRule) +NS_INTERFACE_MAP_END_INHERITING(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSKeyframesRule) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CSSKeyframesRule, css::Rule) + if (tmp->mKeyframeList) { + tmp->mKeyframeList->DropReferences(); + tmp->mKeyframeList = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSKeyframesRule, Rule) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mKeyframeList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +/* virtual */ +bool CSSKeyframesRule::IsCCLeaf() const { + // If we don't have rule list constructed, we are a leaf. + return Rule::IsCCLeaf() && !mKeyframeList; +} + +StyleCssRuleType CSSKeyframesRule::Type() const { + return StyleCssRuleType::Keyframes; +} + +void CSSKeyframesRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + if (mKeyframeList) { + mKeyframeList->SetRawAfterClone(mRawRule); + } +} + +#ifdef DEBUG +/* virtual */ +void CSSKeyframesRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_KeyframesRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +/* virtual */ +void CSSKeyframesRule::DropSheetReference() { + if (mKeyframeList) { + mKeyframeList->DropSheetReference(); + } + css::Rule::DropSheetReference(); +} + +static const uint32_t kRuleNotFound = std::numeric_limits::max(); + +uint32_t CSSKeyframesRule::FindRuleIndexForKey(const nsAString& aKey) { + NS_ConvertUTF16toUTF8 key(aKey); + return Servo_KeyframesRule_FindRule(mRawRule, &key); +} + +template +nsresult CSSKeyframesRule::UpdateRule(Func aCallback) { + if (IsReadOnly()) { + return NS_OK; + } + + StyleSheet* sheet = GetStyleSheet(); + if (sheet) { + sheet->WillDirty(); + } + + aCallback(); + + if (sheet) { + sheet->RuleChanged(this, StyleRuleChangeKind::Generic); + } + + return NS_OK; +} + +void CSSKeyframesRule::GetName(nsAString& aName) const { + nsAtom* name = Servo_KeyframesRule_GetName(mRawRule); + aName = nsDependentAtomString(name); +} + +void CSSKeyframesRule::SetName(const nsAString& aName) { + RefPtr name = NS_Atomize(aName); + nsAtom* oldName = Servo_KeyframesRule_GetName(mRawRule); + if (name == oldName) { + return; + } + + UpdateRule([this, &name]() { + Servo_KeyframesRule_SetName(mRawRule, name.forget().take()); + }); +} + +void CSSKeyframesRule::AppendRule(const nsAString& aRule) { + StyleSheet* sheet = GetStyleSheet(); + if (!sheet) { + // We cannot parse the rule if we don't have a stylesheet. + return; + } + + NS_ConvertUTF16toUTF8 rule(aRule); + UpdateRule([this, sheet, &rule]() { + bool parsedOk = + Servo_KeyframesRule_AppendRule(mRawRule, sheet->RawContents(), &rule); + if (parsedOk && mKeyframeList) { + mKeyframeList->AppendRule(); + } + }); +} + +void CSSKeyframesRule::DeleteRule(const nsAString& aKey) { + auto index = FindRuleIndexForKey(aKey); + if (index == kRuleNotFound) { + return; + } + + UpdateRule([this, index]() { + Servo_KeyframesRule_DeleteRule(mRawRule, index); + if (mKeyframeList) { + mKeyframeList->RemoveRule(index); + } + }); +} + +/* virtual */ +void CSSKeyframesRule::GetCssText(nsACString& aCssText) const { + Servo_KeyframesRule_GetCssText(mRawRule, &aCssText); +} + +/* virtual */ dom::CSSRuleList* CSSKeyframesRule::CssRules() { + return EnsureRules(); +} + +/* virtual */ dom::CSSKeyframeRule* CSSKeyframesRule::IndexedGetter( + uint32_t aIndex, bool& aFound) { + return EnsureRules()->IndexedGetter(aIndex, aFound); +} + +/* virtual */ uint32_t CSSKeyframesRule::Length() { + return EnsureRules()->Length(); +} + +/* virtual */ dom::CSSKeyframeRule* CSSKeyframesRule::FindRule( + const nsAString& aKey) { + auto index = FindRuleIndexForKey(aKey); + if (index != kRuleNotFound) { + return EnsureRules()->GetRule(index); + } + return nullptr; +} + +/* virtual */ +size_t CSSKeyframesRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + if (mKeyframeList) { + n += mKeyframeList->SizeOfIncludingThis(aMallocSizeOf); + } + return n; +} + +/* virtual */ +JSObject* CSSKeyframesRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSKeyframesRule_Binding::Wrap(aCx, this, aGivenProto); +} + +dom::CSSKeyframeList* CSSKeyframesRule::EnsureRules() { + if (!mKeyframeList) { + mKeyframeList = new CSSKeyframeList(do_AddRef(mRawRule), mSheet, this); + } + return mKeyframeList; +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSKeyframesRule.h b/layout/style/CSSKeyframesRule.h new file mode 100644 index 0000000000..191e9a5f18 --- /dev/null +++ b/layout/style/CSSKeyframesRule.h @@ -0,0 +1,70 @@ +/* -*- 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_dom_CSSKeyframesRule_h +#define mozilla_dom_CSSKeyframesRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/dom/CSSKeyframeRule.h" + +namespace mozilla::dom { + +class CSSKeyframeList; + +class CSSKeyframesRule final : public css::Rule { + public: + CSSKeyframesRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSKeyframesRule, css::Rule) + + bool IsCCLeaf() const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + void DropSheetReference() final; + + // WebIDL interface + StyleCssRuleType Type() const final; + const StyleLockedKeyframesRule* Raw() const { return mRawRule.get(); } + void SetRawAfterClone(RefPtr); + + void GetCssText(nsACString& aCssText) const final; + void GetName(nsAString& aName) const; + void SetName(const nsAString& aName); + CSSRuleList* CssRules(); + + CSSKeyframeRule* IndexedGetter(uint32_t aIndex, bool& aFound); + uint32_t Length(); + + void AppendRule(const nsAString& aRule); + void DeleteRule(const nsAString& aKey); + CSSKeyframeRule* FindRule(const nsAString& aKey); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final; + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + private: + CSSKeyframeList* EnsureRules(); + uint32_t FindRuleIndexForKey(const nsAString& aKey); + + template + nsresult UpdateRule(Func aCallback); + + virtual ~CSSKeyframesRule(); + + RefPtr mRawRule; + RefPtr mKeyframeList; // lazily constructed +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSKeyframesRule_h diff --git a/layout/style/CSSLayerBlockRule.cpp b/layout/style/CSSLayerBlockRule.cpp new file mode 100644 index 0000000000..23453fb3af --- /dev/null +++ b/layout/style/CSSLayerBlockRule.cpp @@ -0,0 +1,66 @@ +/* -*- 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 "mozilla/dom/CSSLayerBlockRule.h" +#include "mozilla/dom/CSSLayerBlockRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +CSSLayerBlockRule::CSSLayerBlockRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::GroupRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSLayerBlockRule, + css::GroupRule) + +// QueryInterface implementation for SupportsRule + +#ifdef DEBUG +void CSSLayerBlockRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_LayerBlockRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSLayerBlockRule::Type() const { + return StyleCssRuleType::LayerBlock; +} + +already_AddRefed CSSLayerBlockRule::GetOrCreateRawRules() { + return Servo_LayerBlockRule_GetRules(mRawRule).Consume(); +} + +void CSSLayerBlockRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + css::GroupRule::DidSetRawAfterClone(); +} + +void CSSLayerBlockRule::GetCssText(nsACString& aCssText) const { + Servo_LayerBlockRule_GetCssText(mRawRule.get(), &aCssText); +} + +void CSSLayerBlockRule::GetName(nsACString& aName) const { + Servo_LayerBlockRule_GetName(mRawRule.get(), &aName); +} + +size_t CSSLayerBlockRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +JSObject* CSSLayerBlockRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSLayerBlockRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSLayerBlockRule.h b/layout/style/CSSLayerBlockRule.h new file mode 100644 index 0000000000..fbba832eef --- /dev/null +++ b/layout/style/CSSLayerBlockRule.h @@ -0,0 +1,48 @@ +/* -*- 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_dom_CSSLayerBlockRule_h +#define mozilla_dom_CSSLayerBlockRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSLayerBlockRule final : public css::GroupRule { + public: + CSSLayerBlockRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleLayerBlockRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + + already_AddRefed GetOrCreateRawRules() final; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + + void GetName(nsACString&) const; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + JSObject* WrapObject(JSContext*, JS::Handle) override; + + private: + ~CSSLayerBlockRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSLayerBlockRule_h diff --git a/layout/style/CSSLayerStatementRule.cpp b/layout/style/CSSLayerStatementRule.cpp new file mode 100644 index 0000000000..55e3d6159a --- /dev/null +++ b/layout/style/CSSLayerStatementRule.cpp @@ -0,0 +1,68 @@ +/* -*- 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 "mozilla/dom/CSSLayerStatementRule.h" +#include "mozilla/dom/CSSLayerStatementRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +CSSLayerStatementRule::CSSLayerStatementRule( + RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn) + : Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +NS_IMPL_ADDREF_INHERITED(CSSLayerStatementRule, Rule) +NS_IMPL_RELEASE_INHERITED(CSSLayerStatementRule, Rule) + +// QueryInterface implementation for SupportsRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSLayerStatementRule) +NS_INTERFACE_MAP_END_INHERITING(Rule) + +#ifdef DEBUG +void CSSLayerStatementRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_LayerStatementRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSLayerStatementRule::Type() const { + return StyleCssRuleType::LayerStatement; +} + +void CSSLayerStatementRule::SetRawAfterClone( + RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +void CSSLayerStatementRule::GetCssText(nsACString& aCssText) const { + Servo_LayerStatementRule_GetCssText(mRawRule.get(), &aCssText); +} + +void CSSLayerStatementRule::GetNameList(nsTArray& aNames) const { + size_t size = Servo_LayerStatementRule_GetNameCount(mRawRule.get()); + for (size_t i = 0; i < size; ++i) { + Servo_LayerStatementRule_GetNameAt(mRawRule.get(), i, + aNames.AppendElement()); + } +} + +size_t CSSLayerStatementRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +JSObject* CSSLayerStatementRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSLayerStatementRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSLayerStatementRule.h b/layout/style/CSSLayerStatementRule.h new file mode 100644 index 0000000000..77e5e71255 --- /dev/null +++ b/layout/style/CSSLayerStatementRule.h @@ -0,0 +1,49 @@ +/* -*- 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_dom_CSSLayerStatementRule_h +#define mozilla_dom_CSSLayerStatementRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSLayerStatementRule final : public css::Rule { + public: + CSSLayerStatementRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + + bool IsCCLeaf() const final { return css::Rule::IsCCLeaf(); } + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleLayerStatementRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + + void GetNameList(nsTArray&) const; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + JSObject* WrapObject(JSContext*, JS::Handle) override; + + private: + ~CSSLayerStatementRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSLayerStatementRule_h diff --git a/layout/style/CSSMediaRule.cpp b/layout/style/CSSMediaRule.cpp new file mode 100644 index 0000000000..75b4ce4571 --- /dev/null +++ b/layout/style/CSSMediaRule.cpp @@ -0,0 +1,113 @@ +/* -*- 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 "mozilla/dom/CSSMediaRule.h" + +#include "mozilla/dom/CSSMediaRuleBinding.h" +#include "mozilla/dom/MediaList.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +CSSMediaRule::CSSMediaRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : ConditionRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +CSSMediaRule::~CSSMediaRule() { + if (mMediaList) { + mMediaList->SetStyleSheet(nullptr); + } +} + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSMediaRule, css::ConditionRule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSMediaRule) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CSSMediaRule, + css::ConditionRule) + if (tmp->mMediaList) { + tmp->mMediaList->SetStyleSheet(nullptr); + tmp->mMediaList = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSMediaRule, + css::ConditionRule) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaList) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSMediaRule::IsCCLeaf() const { + return ConditionRule::IsCCLeaf() && !mMediaList; +} + +/* virtual */ +void CSSMediaRule::DropSheetReference() { + if (mMediaList) { + mMediaList->SetStyleSheet(nullptr); + } + ConditionRule::DropSheetReference(); +} + +void CSSMediaRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + if (mMediaList) { + mMediaList->SetRawAfterClone(Servo_MediaRule_GetMedia(mRawRule).Consume()); + mMediaList->SetStyleSheet(nullptr); + mMediaList->SetStyleSheet(GetStyleSheet()); + } + css::ConditionRule::DidSetRawAfterClone(); +} + +already_AddRefed CSSMediaRule::GetOrCreateRawRules() { + return Servo_MediaRule_GetRules(mRawRule).Consume(); +} + +StyleCssRuleType CSSMediaRule::Type() const { return StyleCssRuleType::Media; } + +#ifdef DEBUG +/* virtual */ +void CSSMediaRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_MediaRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +void CSSMediaRule::GetConditionText(nsACString& aConditionText) { + Media()->GetMediaText(aConditionText); +} + +/* virtual */ +void CSSMediaRule::GetCssText(nsACString& aCssText) const { + Servo_MediaRule_GetCssText(mRawRule, &aCssText); +} + +/* virtual */ dom::MediaList* CSSMediaRule::Media() { + if (!mMediaList) { + mMediaList = new MediaList(Servo_MediaRule_GetMedia(mRawRule).Consume()); + mMediaList->SetStyleSheet(GetStyleSheet()); + } + return mMediaList; +} + +/* virtual */ +size_t CSSMediaRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +/* virtual */ +JSObject* CSSMediaRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSMediaRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSMediaRule.h b/layout/style/CSSMediaRule.h new file mode 100644 index 0000000000..d5904ca5cb --- /dev/null +++ b/layout/style/CSSMediaRule.h @@ -0,0 +1,55 @@ +/* -*- 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_dom_CSSMediaRule_h +#define mozilla_dom_CSSMediaRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSMediaRule final : public css::ConditionRule { + public: + CSSMediaRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSMediaRule, css::ConditionRule) + + void DropSheetReference() override; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleMediaRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + already_AddRefed GetOrCreateRawRules() final; + bool IsCCLeaf() const override; + + // WebIDL interface + StyleCssRuleType Type() const override; + // WebIDL interface + void GetCssText(nsACString& aCssText) const final; + void GetConditionText(nsACString& aConditionText) final; + dom::MediaList* Media(); + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + private: + virtual ~CSSMediaRule(); + + RefPtr mRawRule; + RefPtr mMediaList; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSMediaRule_h diff --git a/layout/style/CSSMozDocumentRule.cpp b/layout/style/CSSMozDocumentRule.cpp new file mode 100644 index 0000000000..b59a66d856 --- /dev/null +++ b/layout/style/CSSMozDocumentRule.cpp @@ -0,0 +1,143 @@ +/* -*- 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 "mozilla/dom/CSSMozDocumentRule.h" +#include "mozilla/dom/CSSMozDocumentRuleBinding.h" + +#include "js/RegExpFlags.h" +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/ServoBindings.h" +#include "nsContentUtils.h" +#include "nsHTMLDocument.h" + +namespace mozilla::dom { + +using namespace mozilla::css; + +/* virtual */ +JSObject* CSSMozDocumentRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSMozDocumentRule_Binding::Wrap(aCx, this, aGivenProto); +} + +bool CSSMozDocumentRule::Match(const Document* aDoc, nsIURI* aDocURI, + const nsACString& aDocURISpec, + const nsACString& aPattern, + DocumentMatchingFunction aMatchingFunction) { + switch (aMatchingFunction) { + case DocumentMatchingFunction::MediaDocument: { + auto kind = aDoc->MediaDocumentKind(); + if (aPattern.EqualsLiteral("all")) { + return kind != Document::MediaDocumentKind::NotMedia; + } + MOZ_ASSERT(aPattern.EqualsLiteral("plugin") || + aPattern.EqualsLiteral("image") || + aPattern.EqualsLiteral("video")); + switch (kind) { + case Document::MediaDocumentKind::NotMedia: + return false; + case Document::MediaDocumentKind::Plugin: + return aPattern.EqualsLiteral("plugin"); + case Document::MediaDocumentKind::Image: + return aPattern.EqualsLiteral("image"); + case Document::MediaDocumentKind::Video: + return aPattern.EqualsLiteral("video"); + } + MOZ_ASSERT_UNREACHABLE("Unknown media document kind"); + return false; + } + case DocumentMatchingFunction::URL: + return aDocURISpec == aPattern; + case DocumentMatchingFunction::URLPrefix: + return StringBeginsWith(aDocURISpec, aPattern); + case DocumentMatchingFunction::Domain: { + nsAutoCString host; + if (aDocURI) { + aDocURI->GetHost(host); + } + int32_t lenDiff = host.Length() - aPattern.Length(); + if (lenDiff == 0) { + return host == aPattern; + } + return StringEndsWith(host, aPattern) && host.CharAt(lenDiff - 1) == '.'; + } + case DocumentMatchingFunction::RegExp: { + // Using JS::RegExpFlag::Unicode to allow patterns containing for example + // [^/]. + return nsContentUtils::IsPatternMatching( + NS_ConvertUTF8toUTF16(aDocURISpec), + NS_ConvertUTF8toUTF16(aPattern), aDoc, + /* aHasMultiple = */ false, JS::RegExpFlag::Unicode) + .valueOr(false); + } + case DocumentMatchingFunction::PlainTextDocument: + return aDoc->IsHTMLOrXHTML() && aDoc->AsHTMLDocument()->IsPlainText(); + case DocumentMatchingFunction::UnobservableDocument: { + const BrowsingContext* bc = aDoc->GetBrowsingContext(); + return bc && bc->IsTop() && !bc->HasOpener(); + } + } + MOZ_ASSERT_UNREACHABLE("Unknown matching function"); + return false; +} + +CSSMozDocumentRule::CSSMozDocumentRule(RefPtr aRawRule, + StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : css::ConditionRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +NS_IMPL_ADDREF_INHERITED(CSSMozDocumentRule, css::ConditionRule) +NS_IMPL_RELEASE_INHERITED(CSSMozDocumentRule, css::ConditionRule) + +// QueryInterface implementation for MozDocumentRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSMozDocumentRule) +NS_INTERFACE_MAP_END_INHERITING(css::ConditionRule) + +#ifdef DEBUG +/* virtual */ +void CSSMozDocumentRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_DocumentRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +void CSSMozDocumentRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + css::ConditionRule::DidSetRawAfterClone(); +} + +already_AddRefed +CSSMozDocumentRule::GetOrCreateRawRules() { + return Servo_DocumentRule_GetRules(mRawRule).Consume(); +} + +StyleCssRuleType CSSMozDocumentRule::Type() const { + return StyleCssRuleType::Document; +} + +void CSSMozDocumentRule::GetConditionText(nsACString& aConditionText) { + Servo_DocumentRule_GetConditionText(mRawRule, &aConditionText); +} + +/* virtual */ +void CSSMozDocumentRule::GetCssText(nsACString& aCssText) const { + Servo_DocumentRule_GetCssText(mRawRule, &aCssText); +} + +/* virtual */ +size_t CSSMozDocumentRule::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSMozDocumentRule.h b/layout/style/CSSMozDocumentRule.h new file mode 100644 index 0000000000..0c1b0ab022 --- /dev/null +++ b/layout/style/CSSMozDocumentRule.h @@ -0,0 +1,53 @@ +/* -*- 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_dom_CSSMozDocumentRule_h +#define mozilla_dom_CSSMozDocumentRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/css/DocumentMatchingFunction.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSMozDocumentRule final : public css::ConditionRule { + public: + CSSMozDocumentRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + + static bool Match(const Document*, nsIURI* aDocURI, + const nsACString& aDocURISpec, const nsACString& aPattern, + css::DocumentMatchingFunction); + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleDocumentRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + already_AddRefed GetOrCreateRawRules() final; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + void GetConditionText(nsACString& aConditionText) final; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + private: + ~CSSMozDocumentRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSMozDocumentRule_h diff --git a/layout/style/CSSNamespaceRule.cpp b/layout/style/CSSNamespaceRule.cpp new file mode 100644 index 0000000000..5baa2bba0b --- /dev/null +++ b/layout/style/CSSNamespaceRule.cpp @@ -0,0 +1,51 @@ +/* -*- 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 "mozilla/dom/CSSNamespaceRule.h" + +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +CSSNamespaceRule::~CSSNamespaceRule() = default; + +#ifdef DEBUG +void CSSNamespaceRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_NamespaceRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSNamespaceRule::Type() const { + return StyleCssRuleType::Namespace; +} + +nsAtom* CSSNamespaceRule::GetPrefix() const { + return Servo_NamespaceRule_GetPrefix(mRawRule); +} + +void CSSNamespaceRule::GetURLSpec(nsString& aURLSpec) const { + nsAtom* atom = Servo_NamespaceRule_GetURI(mRawRule); + atom->ToString(aURLSpec); +} + +void CSSNamespaceRule::GetCssText(nsACString& aCssText) const { + Servo_NamespaceRule_GetCssText(mRawRule, &aCssText); +} + +void CSSNamespaceRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +size_t CSSNamespaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSNamespaceRule.h b/layout/style/CSSNamespaceRule.h new file mode 100644 index 0000000000..ff3b5f8b5b --- /dev/null +++ b/layout/style/CSSNamespaceRule.h @@ -0,0 +1,63 @@ +/* -*- 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_dom_CSSNamespaceRule_h +#define mozilla_dom_CSSNamespaceRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/dom/CSSNamespaceRuleBinding.h" +#include "mozilla/ServoBindingTypes.h" + +class nsAtom; + +namespace mozilla::dom { + +class CSSNamespaceRule final : public css::Rule { + public: + CSSNamespaceRule(already_AddRefed aRule, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRule)) {} + + bool IsCCLeaf() const final { return Rule::IsCCLeaf(); } + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + nsAtom* GetPrefix() const; + void GetURLSpec(nsString& aURLSpec) const; + + // WebIDL interface + void GetCssText(nsACString& aCssText) const final; + + StyleCssRuleType Type() const final; + + const StyleNamespaceRule* Raw() const { return mRawRule.get(); } + void SetRawAfterClone(RefPtr); + + void GetNamespaceURI(nsString& aNamespaceURI) { GetURLSpec(aNamespaceURI); } + + void GetPrefix(DOMString& aPrefix) { + aPrefix.SetKnownLiveAtom(GetPrefix(), DOMString::eTreatNullAsEmpty); + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) final { + return CSSNamespaceRule_Binding::Wrap(aCx, this, aGivenProto); + } + + private: + ~CSSNamespaceRule(); + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSNamespaceRule_h diff --git a/layout/style/CSSPageRule.cpp b/layout/style/CSSPageRule.cpp new file mode 100644 index 0000000000..04d064b362 --- /dev/null +++ b/layout/style/CSSPageRule.cpp @@ -0,0 +1,198 @@ +/* -*- 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 "mozilla/dom/CSSPageRule.h" +#include "mozilla/dom/CSSPageRuleBinding.h" + +#include "mozilla/DeclarationBlock.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +// -- CSSPageRuleDeclaration --------------------------------------- + +CSSPageRuleDeclaration::CSSPageRuleDeclaration( + already_AddRefed aDecls) + : mDecls(new DeclarationBlock(std::move(aDecls))) { + mDecls->SetOwningRule(Rule()); +} + +CSSPageRuleDeclaration::~CSSPageRuleDeclaration() { + mDecls->SetOwningRule(nullptr); +} + +// QueryInterface implementation for CSSPageRuleDeclaration +NS_INTERFACE_MAP_BEGIN(CSSPageRuleDeclaration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + // We forward the cycle collection interfaces to Rule(), which is + // never null (in fact, we're part of that object!) + if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || + aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { + return Rule()->QueryInterface(aIID, aInstancePtr); + } +NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) + +NS_IMPL_ADDREF_USING_AGGREGATOR(CSSPageRuleDeclaration, Rule()) +NS_IMPL_RELEASE_USING_AGGREGATOR(CSSPageRuleDeclaration, Rule()) + +/* nsDOMCSSDeclaration implementation */ + +css::Rule* CSSPageRuleDeclaration::GetParentRule() { return Rule(); } + +nsINode* CSSPageRuleDeclaration::GetAssociatedNode() const { + return Rule()->GetAssociatedDocumentOrShadowRoot(); +} + +nsISupports* CSSPageRuleDeclaration::GetParentObject() const { + return Rule()->GetParentObject(); +} + +DeclarationBlock* CSSPageRuleDeclaration::GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) { + if (aOperation != Operation::Read) { + if (StyleSheet* sheet = Rule()->GetStyleSheet()) { + sheet->WillDirty(); + } + } + return mDecls; +} + +void CSSPageRuleDeclaration::SetRawAfterClone( + RefPtr aDeclarationBlock) { + mDecls->SetOwningRule(nullptr); + mDecls = new DeclarationBlock(aDeclarationBlock.forget()); + mDecls->SetOwningRule(Rule()); +} + +nsresult CSSPageRuleDeclaration::SetCSSDeclaration( + DeclarationBlock* aDecl, MutationClosureData* aClosureData) { + MOZ_ASSERT(aDecl, "must be non-null"); + CSSPageRule* rule = Rule(); + + if (aDecl != mDecls) { + mDecls->SetOwningRule(nullptr); + RefPtr decls = aDecl; + Servo_PageRule_SetStyle(rule->Raw(), decls->Raw()); + mDecls = std::move(decls); + mDecls->SetOwningRule(rule); + } + + return NS_OK; +} + +nsDOMCSSDeclaration::ParsingEnvironment +CSSPageRuleDeclaration::GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const { + return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Page); +} + +// -- CSSPageRule -------------------------------------------------- + +CSSPageRule::CSSPageRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)), + mDecls(Servo_PageRule_GetStyle(mRawRule).Consume()) {} + +NS_IMPL_ADDREF_INHERITED(CSSPageRule, css::Rule) +NS_IMPL_RELEASE_INHERITED(CSSPageRule, css::Rule) + +// QueryInterface implementation for PageRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSPageRule) +NS_INTERFACE_MAP_END_INHERITING(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSPageRule) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSPageRule, css::Rule) + // Keep this in sync with IsCCLeaf. + + // Trace the wrapper for our declaration. This just expands out + // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use + // directly because the wrapper is on the declaration, not on us. + tmp->mDecls.TraceWrapper(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSPageRule) + // Keep this in sync with IsCCLeaf. + + // Unlink the wrapper for our declaration. + // + // Note that this has to happen before unlinking css::Rule. + tmp->UnlinkDeclarationWrapper(tmp->mDecls); + tmp->mDecls.mDecls->SetOwningRule(nullptr); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::Rule) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSPageRule, css::Rule) + // Keep this in sync with IsCCLeaf. +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSPageRule::IsCCLeaf() const { + if (!Rule::IsCCLeaf()) { + return false; + } + + return !mDecls.PreservingWrapper(); +} + +void CSSPageRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + mDecls.SetRawAfterClone(Servo_PageRule_GetStyle(mRawRule.get()).Consume()); +} + +StyleCssRuleType CSSPageRule::Type() const { return StyleCssRuleType::Page; } + +size_t CSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +#ifdef DEBUG +void CSSPageRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_PageRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +/* CSSRule implementation */ + +void CSSPageRule::GetCssText(nsACString& aCssText) const { + Servo_PageRule_GetCssText(mRawRule, &aCssText); +} + +/* CSSPageRule implementation */ + +void CSSPageRule::GetSelectorText(nsACString& aSelectorText) const { + Servo_PageRule_GetSelectorText(mRawRule.get(), &aSelectorText); +} + +void CSSPageRule::SetSelectorText(const nsACString& aSelectorText) { + if (IsReadOnly()) { + return; + } + + if (StyleSheet* const sheet = GetStyleSheet()) { + sheet->WillDirty(); + const StyleStylesheetContents* const contents = sheet->RawContents(); + if (Servo_PageRule_SetSelectorText(contents, mRawRule.get(), + &aSelectorText)) { + sheet->RuleChanged(this, StyleRuleChangeKind::Generic); + } + } +} + +nsICSSDeclaration* CSSPageRule::Style() { return &mDecls; } + +JSObject* CSSPageRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSPageRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSPageRule.h b/layout/style/CSSPageRule.h new file mode 100644 index 0000000000..705d526bdf --- /dev/null +++ b/layout/style/CSSPageRule.h @@ -0,0 +1,108 @@ +/* -*- 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_dom_CSSPageRule_h +#define mozilla_dom_CSSPageRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +#include "nsDOMCSSDeclaration.h" +#include "nsICSSDeclaration.h" + +namespace mozilla { +class DeclarationBlock; + +namespace dom { +class DocGroup; +class CSSPageRule; + +class CSSPageRuleDeclaration final : public nsDOMCSSDeclaration { + public: + NS_DECL_ISUPPORTS_INHERITED + + css::Rule* GetParentRule() final; + nsINode* GetAssociatedNode() const final; + nsISupports* GetParentObject() const final; + + protected: + DeclarationBlock* GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) final; + nsresult SetCSSDeclaration(DeclarationBlock* aDecl, + MutationClosureData* aClosureData) final; + Document* DocToUpdate() final { return nullptr; } + nsDOMCSSDeclaration::ParsingEnvironment GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const final; + + private: + // For accessing the constructor. + friend class CSSPageRule; + + explicit CSSPageRuleDeclaration( + already_AddRefed aDecls); + void SetRawAfterClone(RefPtr); + + ~CSSPageRuleDeclaration(); + + inline CSSPageRule* Rule(); + inline const CSSPageRule* Rule() const; + + RefPtr mDecls; +}; + +class CSSPageRule final : public css::Rule { + public: + CSSPageRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSPageRule, css::Rule) + + bool IsCCLeaf() const final; + + StyleLockedPageRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + + // WebIDL interfaces + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + nsICSSDeclaration* Style(); + + void GetSelectorText(nsACString& aSelectorText) const; + void SetSelectorText(const nsACString& aSelectorText); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + private: + ~CSSPageRule() = default; + + // For computing the offset of mDecls. + friend class CSSPageRuleDeclaration; + + RefPtr mRawRule; + CSSPageRuleDeclaration mDecls; +}; + +CSSPageRule* CSSPageRuleDeclaration::Rule() { + return reinterpret_cast(reinterpret_cast(this) - + offsetof(CSSPageRule, mDecls)); +} + +const CSSPageRule* CSSPageRuleDeclaration::Rule() const { + return reinterpret_cast( + reinterpret_cast(this) - offsetof(CSSPageRule, mDecls)); +} + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CSSPageRule_h diff --git a/layout/style/CSSPropFlags.h b/layout/style/CSSPropFlags.h new file mode 100644 index 0000000000..a2bcff417e --- /dev/null +++ b/layout/style/CSSPropFlags.h @@ -0,0 +1,68 @@ +/* -*- 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_CSSPropFlags_h +#define mozilla_CSSPropFlags_h + +#include "mozilla/TypedEnumBits.h" + +namespace mozilla { + +enum class CSSPropFlags : uint16_t { + // This property is not parsed. It is only there for internal use like + // attribute mapping. + Inaccessible = 1 << 0, + + // The following two flags along with the pref defines where the this + // property can be used: + // * If none of the two flags is presented, the pref completely controls + // the availability of this property. And in that case, if it has no + // pref, this property is usable everywhere. + // * If any of the flags is set, this property is always enabled in the + // specific contexts regardless of the value of the pref. If there is + // no pref for this property at all in this case, it is an internal- + // only property, which cannot be used anywhere else, and should be + // wrapped in "#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL". + // Note that, these flags have no effect on the use of aliases of this + // property. + // Furthermore, for the purposes of animation (including triggering + // transitions) these flags are ignored. That is, if the property is disabled + // by a pref, we will *not* run animations or transitions on it even in + // UA sheets or chrome. + EnabledInUASheets = 1 << 1, + EnabledInChrome = 1 << 2, + EnabledInUASheetsAndChrome = EnabledInUASheets | EnabledInChrome, + EnabledMask = EnabledInUASheetsAndChrome, + + // This property can be animated on the compositor. + CanAnimateOnCompositor = 1 << 3, + + // This property is an internal property that is not represented in + // the DOM. Properties with this flag are defined in an #ifndef + // CSS_PROP_LIST_EXCLUDE_INTERNAL section. + Internal = 1 << 4, + + // Whether this property should be serialized by Servo in getComputedStyle. + SerializedByServo = 1 << 5, + + // Whether this is a logical property. + IsLogical = 1 << 6, + + // Whether this shorthand property is unconditionally exposed in + // getComputedStyle. + ShorthandUnconditionallyExposedOnGetCS = 1 << 7, + + // Whether this property, when changed, may affect layout, overflow, or paint. + AffectsLayout = 1 << 8, + AffectsOverflow = 1 << 9, + AffectsPaint = 1 << 10, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSPropFlags) + +} // namespace mozilla + +#endif // mozilla_CSSPropFlags_h diff --git a/layout/style/CSSPropertyRule.cpp b/layout/style/CSSPropertyRule.cpp new file mode 100644 index 0000000000..019e008b08 --- /dev/null +++ b/layout/style/CSSPropertyRule.cpp @@ -0,0 +1,72 @@ +/* -*- 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 "mozilla/dom/CSSPropertyRule.h" +#include "mozilla/dom/CSSPropertyRuleBinding.h" +#include "mozilla/ServoBindings.h" + +namespace mozilla::dom { + +bool CSSPropertyRule::IsCCLeaf() const { return Rule::IsCCLeaf(); } + +void CSSPropertyRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); +} + +/* virtual */ +JSObject* CSSPropertyRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSPropertyRule_Binding::Wrap(aCx, this, aGivenProto); +} + +#ifdef DEBUG +void CSSPropertyRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_PropertyRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +size_t CSSPropertyRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +StyleCssRuleType CSSPropertyRule::Type() const { + return StyleCssRuleType::Property; +} + +/* CSSRule implementation */ + +void CSSPropertyRule::GetCssText(nsACString& aCssText) const { + Servo_PropertyRule_GetCssText(mRawRule, &aCssText); +} + +/* CSSPropertyRule implementation */ + +void CSSPropertyRule::GetName(nsACString& aNameStr) const { + Servo_PropertyRule_GetName(mRawRule, &aNameStr); +} + +void CSSPropertyRule::GetSyntax(nsACString& aSyntaxStr) const { + Servo_PropertyRule_GetSyntax(mRawRule, &aSyntaxStr); +} + +bool CSSPropertyRule::Inherits() const { + return Servo_PropertyRule_GetInherits(mRawRule); +} + +void CSSPropertyRule::GetInitialValue(nsACString& aInitialValueStr) const { + bool found = Servo_PropertyRule_GetInitialValue(mRawRule, &aInitialValueStr); + if (!found) { + aInitialValueStr.SetIsVoid(true); + } +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSPropertyRule.h b/layout/style/CSSPropertyRule.h new file mode 100644 index 0000000000..ca6d5a12f2 --- /dev/null +++ b/layout/style/CSSPropertyRule.h @@ -0,0 +1,61 @@ +/* -*- 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_dom_CSSPropertyRule_h +#define mozilla_dom_CSSPropertyRule_h + +#include "mozilla/css/Rule.h" +#include "mozilla/ServoBindingTypes.h" + +#include "nsICSSDeclaration.h" + +struct StylePropertyRule; + +namespace mozilla::dom { + +class CSSPropertyRule final : public css::Rule { + public: + CSSPropertyRule(already_AddRefed aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn) + : css::Rule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + + bool IsCCLeaf() const final; + + StylePropertyRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr aRaw); + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; + + // WebIDL interfaces + StyleCssRuleType Type() const final; + + void GetName(nsACString& aName) const; + + void GetSyntax(nsACString& aSyntax) const; + + bool Inherits() const; + + void GetInitialValue(nsACString& aInitialValueStr) const; + + void GetCssText(nsACString& aCssText) const final; + + private: + ~CSSPropertyRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSPropertyRule_h diff --git a/layout/style/CSSRuleList.cpp b/layout/style/CSSRuleList.cpp new file mode 100644 index 0000000000..e4e9179022 --- /dev/null +++ b/layout/style/CSSRuleList.cpp @@ -0,0 +1,31 @@ +/* -*- 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 "mozilla/dom/CSSRuleList.h" + +#include "mozilla/dom/CSSRuleListBinding.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(CSSRuleList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSRuleList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(CSSRuleList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CSSRuleList) + +/* virtual */ +JSObject* CSSRuleList::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSRuleList_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/CSSRuleList.h b/layout/style/CSSRuleList.h new file mode 100644 index 0000000000..b3802233d0 --- /dev/null +++ b/layout/style/CSSRuleList.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/. */ + +#ifndef mozilla_dom_CSSRuleList_h +#define mozilla_dom_CSSRuleList_h + +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Rule.h" +#include "nsWrapperCache.h" + +namespace mozilla::dom { + +class CSSRuleList : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(CSSRuleList) + + virtual StyleSheet* GetParentObject() = 0; + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + + // WebIDL API + css::Rule* Item(uint32_t aIndex) { + bool unused; + return IndexedGetter(aIndex, unused); + } + + virtual css::Rule* IndexedGetter(uint32_t aIndex, bool& aFound) = 0; + virtual uint32_t Length() = 0; + + protected: + virtual ~CSSRuleList() = default; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_CSSRuleList_h */ diff --git a/layout/style/CSSStyleRule.cpp b/layout/style/CSSStyleRule.cpp new file mode 100644 index 0000000000..5cdb47fddb --- /dev/null +++ b/layout/style/CSSStyleRule.cpp @@ -0,0 +1,323 @@ +/* -*- 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 "mozilla/dom/CSSStyleRule.h" + +#include "mozilla/CSSEnabledState.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/PseudoStyleType.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/dom/ShadowRoot.h" +#include "nsCSSPseudoElements.h" + +#include "mozAutoDocUpdate.h" +#include "nsISupports.h" + +namespace mozilla::dom { + +// -- CSSStyleRuleDeclaration --------------------------------------- + +CSSStyleRuleDeclaration::CSSStyleRuleDeclaration( + already_AddRefed aDecls) + : mDecls(new DeclarationBlock(std::move(aDecls))) { + mDecls->SetOwningRule(Rule()); +} + +CSSStyleRuleDeclaration::~CSSStyleRuleDeclaration() { + mDecls->SetOwningRule(nullptr); +} + +// QueryInterface implementation for CSSStyleRuleDeclaration +NS_INTERFACE_MAP_BEGIN(CSSStyleRuleDeclaration) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + // We forward the cycle collection interfaces to Rule(), which is + // never null (in fact, we're part of that object!) + if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || + aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { + return Rule()->QueryInterface(aIID, aInstancePtr); + } +NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) + +NS_IMPL_ADDREF_USING_AGGREGATOR(CSSStyleRuleDeclaration, Rule()) +NS_IMPL_RELEASE_USING_AGGREGATOR(CSSStyleRuleDeclaration, Rule()) + +/* nsDOMCSSDeclaration implementation */ + +css::Rule* CSSStyleRuleDeclaration::GetParentRule() { return Rule(); } + +nsINode* CSSStyleRuleDeclaration::GetAssociatedNode() const { + return Rule()->GetAssociatedDocumentOrShadowRoot(); +} + +nsISupports* CSSStyleRuleDeclaration::GetParentObject() const { + return Rule()->GetParentObject(); +} + +DeclarationBlock* CSSStyleRuleDeclaration::GetOrCreateCSSDeclaration( + Operation aOperation, DeclarationBlock** aCreated) { + if (aOperation != Operation::Read) { + if (StyleSheet* sheet = Rule()->GetStyleSheet()) { + sheet->WillDirty(); + } + } + return mDecls; +} + +void CSSStyleRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + mDecls.SetRawAfterClone(Servo_StyleRule_GetStyle(mRawRule).Consume()); + GroupRule::DidSetRawAfterClone(); +} + +already_AddRefed CSSStyleRule::GetOrCreateRawRules() { + return Servo_StyleRule_EnsureRules(mRawRule, IsReadOnly()).Consume(); +} + +void CSSStyleRuleDeclaration::SetRawAfterClone( + RefPtr aRaw) { + RefPtr block = new DeclarationBlock(aRaw.forget()); + mDecls->SetOwningRule(nullptr); + mDecls = std::move(block); + mDecls->SetOwningRule(Rule()); +} + +nsresult CSSStyleRuleDeclaration::SetCSSDeclaration( + DeclarationBlock* aDecl, MutationClosureData* aClosureData) { + CSSStyleRule* rule = Rule(); + + if (StyleSheet* sheet = rule->GetStyleSheet()) { + if (aDecl != mDecls) { + mDecls->SetOwningRule(nullptr); + RefPtr decls = aDecl; + Servo_StyleRule_SetStyle(rule->Raw(), decls->Raw()); + mDecls = std::move(decls); + mDecls->SetOwningRule(rule); + } + sheet->RuleChanged(rule, StyleRuleChangeKind::StyleRuleDeclarations); + } + return NS_OK; +} + +Document* CSSStyleRuleDeclaration::DocToUpdate() { return nullptr; } + +nsDOMCSSDeclaration::ParsingEnvironment +CSSStyleRuleDeclaration::GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const { + return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Style); +} + +// -- CSSStyleRule -------------------------------------------------- + +CSSStyleRule::CSSStyleRule(already_AddRefed aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : GroupRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(aRawRule), + mDecls(Servo_StyleRule_GetStyle(mRawRule).Consume()) {} + +NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSStyleRule, GroupRule) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSStyleRule) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSStyleRule, GroupRule) + // Keep this in sync with IsCCLeaf. + + // Trace the wrapper for our declaration. This just expands out + // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use + // directly because the wrapper is on the declaration, not on us. + tmp->mDecls.TraceWrapper(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSStyleRule) + // Keep this in sync with IsCCLeaf. + + // Unlink the wrapper for our declaration. + // + // Note that this has to happen before unlinking css::Rule. + tmp->UnlinkDeclarationWrapper(tmp->mDecls); + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(GroupRule) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSStyleRule, GroupRule) + // Keep this in sync with IsCCLeaf. +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +bool CSSStyleRule::IsCCLeaf() const { + if (!GroupRule::IsCCLeaf()) { + return false; + } + return !mDecls.PreservingWrapper(); +} + +size_t CSSStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - mRawRule + // - mDecls + + return n; +} + +#ifdef DEBUG +void CSSStyleRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_StyleRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +/* CSSRule implementation */ + +StyleCssRuleType CSSStyleRule::Type() const { return StyleCssRuleType::Style; } + +void CSSStyleRule::GetCssText(nsACString& aCssText) const { + Servo_StyleRule_GetCssText(mRawRule, &aCssText); +} + +nsICSSDeclaration* CSSStyleRule::Style() { return &mDecls; } + +/* CSSStyleRule implementation */ + +void CSSStyleRule::GetSelectorText(nsACString& aSelectorText) { + Servo_StyleRule_GetSelectorText(mRawRule, &aSelectorText); +} + +void CSSStyleRule::SetSelectorText(const nsACString& aSelectorText) { + if (IsReadOnly()) { + return; + } + + if (StyleSheet* sheet = GetStyleSheet()) { + sheet->WillDirty(); + + // TODO(emilio): May actually be more efficient to handle this as rule + // removal + addition, from the point of view of invalidation... + const StyleStylesheetContents* contents = sheet->RawContents(); + if (Servo_StyleRule_SetSelectorText(contents, mRawRule, &aSelectorText)) { + sheet->RuleChanged(this, StyleRuleChangeKind::Generic); + } + } +} + +uint32_t CSSStyleRule::SelectorCount() const { + return Servo_StyleRule_GetSelectorCount(mRawRule); +} + +static void CollectStyleRules(CSSStyleRule& aDeepestRule, bool aDesugared, + nsTArray& aResult) { + aResult.AppendElement(aDeepestRule.Raw()); + if (aDesugared) { + for (auto* rule = aDeepestRule.GetParentRule(); rule; + rule = rule->GetParentRule()) { + if (rule->Type() == StyleCssRuleType::Style) { + aResult.AppendElement(static_cast(rule)->Raw()); + } + } + } +} + +void CSSStyleRule::GetSelectorDataAtIndex(uint32_t aSelectorIndex, + bool aDesugared, nsACString* aText, + uint64_t* aSpecificity) { + AutoTArray rules; + CollectStyleRules(*this, aDesugared, rules); + Servo_StyleRule_GetSelectorDataAtIndex(&rules, aSelectorIndex, aText, + aSpecificity); +} + +void CSSStyleRule::SelectorTextAt(uint32_t aSelectorIndex, bool aDesugared, + nsACString& aText) { + GetSelectorDataAtIndex(aSelectorIndex, aDesugared, &aText, nullptr); +} + +uint64_t CSSStyleRule::SelectorSpecificityAt(uint32_t aSelectorIndex, + bool aDesugared) { + uint64_t s = 0; + GetSelectorDataAtIndex(aSelectorIndex, aDesugared, nullptr, &s); + return s; +} + +bool CSSStyleRule::SelectorMatchesElement(uint32_t aSelectorIndex, + Element& aElement, + const nsAString& aPseudo, + bool aRelevantLinkVisited) { + Maybe pseudoType = nsCSSPseudoElements::GetPseudoType( + aPseudo, CSSEnabledState::IgnoreEnabledState); + if (!pseudoType) { + return false; + } + + auto* host = [&]() -> Element* { + auto* sheet = GetStyleSheet(); + if (!sheet) { + return nullptr; + } + if (auto* owner = sheet->GetAssociatedDocumentOrShadowRoot()) { + if (auto* shadow = ShadowRoot::FromNode(owner->AsNode())) { + return shadow->Host(); + } + } + for (auto* adopter : sheet->SelfOrAncestorAdopters()) { + // Try to guess. This is not fully correct but it's the best we can do + // with the info at hand... + auto* shadow = ShadowRoot::FromNode(adopter->AsNode()); + if (!shadow) { + continue; + } + if (shadow->Host() == &aElement || + shadow == aElement.GetContainingShadow()) { + return shadow->Host(); + } + } + return nullptr; + }(); + + AutoTArray rules; + CollectStyleRules(*this, /* aDesugared = */ true, rules); + + return Servo_StyleRule_SelectorMatchesElement( + &rules, &aElement, aSelectorIndex, host, *pseudoType, + aRelevantLinkVisited); +} + +NotNull CSSStyleRule::GetDeclarationBlock() const { + return WrapNotNull(mDecls.mDecls); +} + +SelectorWarningKind ToWebIDLSelectorWarningKind( + StyleSelectorWarningKind aKind) { + switch (aKind) { + case StyleSelectorWarningKind::UnconstraintedRelativeSelector: + return SelectorWarningKind::UnconstrainedHas; + } + MOZ_ASSERT_UNREACHABLE("Unhandled selector warning kind"); + // Return something for assert-disabled builds. + return SelectorWarningKind::UnconstrainedHas; +} + +void CSSStyleRule::GetSelectorWarnings( + nsTArray& aResult) const { + nsTArray result; + Servo_GetSelectorWarnings(mRawRule, &result); + for (const auto& warning : result) { + auto& entry = *aResult.AppendElement(); + entry.mIndex = warning.index; + entry.mKind = ToWebIDLSelectorWarningKind(warning.kind); + } +} + +/* virtual */ +JSObject* CSSStyleRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSStyleRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSStyleRule.h b/layout/style/CSSStyleRule.h new file mode 100644 index 0000000000..05eaae5c10 --- /dev/null +++ b/layout/style/CSSStyleRule.h @@ -0,0 +1,125 @@ +/* -*- 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_CSSStyleRule_h +#define mozilla_CSSStyleRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/NotNull.h" +#include "mozilla/WeakPtr.h" +#include "mozilla/dom/CSSStyleRuleBinding.h" + +#include "nsDOMCSSDeclaration.h" + +namespace mozilla { + +class DeclarationBlock; + +namespace dom { +class DocGroup; +class CSSStyleRule; + +class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration { + public: + NS_DECL_ISUPPORTS_INHERITED + + css::Rule* GetParentRule() final; + nsINode* GetAssociatedNode() const final; + nsISupports* GetParentObject() const final; + + protected: + mozilla::DeclarationBlock* GetOrCreateCSSDeclaration( + Operation aOperation, mozilla::DeclarationBlock** aCreated) final; + nsresult SetCSSDeclaration(DeclarationBlock* aDecl, + MutationClosureData* aClosureData) final; + Document* DocToUpdate() final; + ParsingEnvironment GetParsingEnvironment( + nsIPrincipal* aSubjectPrincipal) const final; + + private: + // For accessing the constructor. + friend class CSSStyleRule; + + explicit CSSStyleRuleDeclaration( + already_AddRefed aDecls); + ~CSSStyleRuleDeclaration(); + + inline CSSStyleRule* Rule(); + inline const CSSStyleRule* Rule() const; + + void SetRawAfterClone(RefPtr); + + RefPtr mDecls; +}; + +class CSSStyleRule final : public css::GroupRule, public SupportsWeakPtr { + public: + CSSStyleRule(already_AddRefed aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine, + uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSStyleRule, + css::GroupRule) + bool IsCCLeaf() const final MOZ_MUST_OVERRIDE; + + uint32_t SelectorCount() const; + void SelectorTextAt(uint32_t aSelectorIndex, bool aDesugared, + nsACString& aText); + uint64_t SelectorSpecificityAt(uint32_t aSelectorIndex, bool aDesugared); + bool SelectorMatchesElement(uint32_t aSelectorIndex, dom::Element&, + const nsAString& aPseudo, + bool aRelevantLinkVisited); + NotNull GetDeclarationBlock() const; + void GetSelectorWarnings(nsTArray& aResult) const; + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + void GetSelectorText(nsACString& aSelectorText); + void SetSelectorText(const nsACString& aSelectorText); + nsICSSDeclaration* Style(); + + StyleLockedStyleRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + already_AddRefed GetOrCreateRawRules() final; + + // Methods of mozilla::css::Rule + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final; +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + JSObject* WrapObject(JSContext*, JS::Handle aGivenProto) override; + + private: + ~CSSStyleRule() = default; + + void GetSelectorDataAtIndex(uint32_t aSelectorIndex, bool aDesugared, + nsACString* aText, uint64_t* aSpecificity); + + // For computing the offset of mDecls. + friend class CSSStyleRuleDeclaration; + + RefPtr mRawRule; + CSSStyleRuleDeclaration mDecls; +}; + +CSSStyleRule* CSSStyleRuleDeclaration::Rule() { + return reinterpret_cast(reinterpret_cast(this) - + offsetof(CSSStyleRule, mDecls)); +} + +const CSSStyleRule* CSSStyleRuleDeclaration::Rule() const { + return reinterpret_cast( + reinterpret_cast(this) - offsetof(CSSStyleRule, mDecls)); +} + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_CSSStyleRule_h diff --git a/layout/style/CSSSupportsRule.cpp b/layout/style/CSSSupportsRule.cpp new file mode 100644 index 0000000000..e848258ed8 --- /dev/null +++ b/layout/style/CSSSupportsRule.cpp @@ -0,0 +1,76 @@ +/* -*- 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 "mozilla/dom/CSSSupportsRule.h" + +#include "mozilla/css/GroupRule.h" +#include "mozilla/dom/CSSSupportsRuleBinding.h" +#include "mozilla/ServoBindings.h" + +using namespace mozilla::css; + +namespace mozilla::dom { + +CSSSupportsRule::CSSSupportsRule(RefPtr aRawRule, + StyleSheet* aSheet, css::Rule* aParentRule, + uint32_t aLine, uint32_t aColumn) + : css::ConditionRule(aSheet, aParentRule, aLine, aColumn), + mRawRule(std::move(aRawRule)) {} + +NS_IMPL_ADDREF_INHERITED(CSSSupportsRule, ConditionRule) +NS_IMPL_RELEASE_INHERITED(CSSSupportsRule, ConditionRule) + +// QueryInterface implementation for SupportsRule +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CSSSupportsRule) +NS_INTERFACE_MAP_END_INHERITING(ConditionRule) + +#ifdef DEBUG +/* virtual */ +void CSSSupportsRule::List(FILE* out, int32_t aIndent) const { + nsAutoCString str; + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + Servo_SupportsRule_Debug(mRawRule, &str); + fprintf_stderr(out, "%s\n", str.get()); +} +#endif + +StyleCssRuleType CSSSupportsRule::Type() const { + return StyleCssRuleType::Supports; +} + +void CSSSupportsRule::GetConditionText(nsACString& aConditionText) { + Servo_SupportsRule_GetConditionText(mRawRule, &aConditionText); +} + +/* virtual */ +void CSSSupportsRule::GetCssText(nsACString& aCssText) const { + Servo_SupportsRule_GetCssText(mRawRule, &aCssText); +} + +void CSSSupportsRule::SetRawAfterClone(RefPtr aRaw) { + mRawRule = std::move(aRaw); + css::ConditionRule::DidSetRawAfterClone(); +} + +already_AddRefed CSSSupportsRule::GetOrCreateRawRules() { + return Servo_SupportsRule_GetRules(mRawRule).Consume(); +} + +/* virtual */ +size_t CSSSupportsRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO Implement this! + return aMallocSizeOf(this); +} + +/* virtual */ +JSObject* CSSSupportsRule::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return CSSSupportsRule_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/layout/style/CSSSupportsRule.h b/layout/style/CSSSupportsRule.h new file mode 100644 index 0000000000..58b622c96f --- /dev/null +++ b/layout/style/CSSSupportsRule.h @@ -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/. */ + +#ifndef mozilla_dom_CSSSupportsRule_h +#define mozilla_dom_CSSSupportsRule_h + +#include "mozilla/css/GroupRule.h" +#include "mozilla/ServoBindingTypes.h" + +namespace mozilla::dom { + +class CSSSupportsRule final : public css::ConditionRule { + public: + CSSSupportsRule(RefPtr aRawRule, StyleSheet* aSheet, + css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn); + + NS_DECL_ISUPPORTS_INHERITED + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const final; +#endif + + StyleSupportsRule* Raw() const { return mRawRule; } + void SetRawAfterClone(RefPtr); + + // WebIDL interface + StyleCssRuleType Type() const final; + void GetCssText(nsACString& aCssText) const final; + void GetConditionText(nsACString& aConditionText) final; + already_AddRefed GetOrCreateRawRules() final; + + size_t SizeOfIncludingThis(MallocSizeOf) const override; + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + private: + ~CSSSupportsRule() = default; + + RefPtr mRawRule; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_CSSSupportsRule_h diff --git a/layout/style/CSSValue.h b/layout/style/CSSValue.h new file mode 100644 index 0000000000..532d9a07df --- /dev/null +++ b/layout/style/CSSValue.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/. */ + +/* DOM object representing values in DOM computed style */ + +#ifndef mozilla_dom_CSSValue_h_ +#define mozilla_dom_CSSValue_h_ + +#include "nsStringFwd.h" +#include "mozilla/RefCounted.h" + +class nsROCSSPrimitiveValue; +namespace mozilla { +class ErrorResult; +} // namespace mozilla + +namespace mozilla::dom { + +/** + * CSSValue - a DOM object representing values in DOM computed style. + */ +class CSSValue : public RefCounted { + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(CSSValue); + enum : uint16_t { + CSS_INHERIT, + CSS_PRIMITIVE_VALUE, + CSS_VALUE_LIST, + CSS_CUSTOM, + }; + + // CSSValue + virtual void GetCssText(nsAString&) = 0; + virtual uint16_t CssValueType() const = 0; + + virtual ~CSSValue() = default; + + // Downcasting + + /** + * Return this as a nsROCSSPrimitiveValue* if its a primitive value, and null + * otherwise. + * + * Defined in nsROCSSPrimitiveValue.h. + */ + inline nsROCSSPrimitiveValue* AsPrimitiveValue(); +}; + +} // namespace mozilla::dom + +#endif diff --git a/layout/style/CachedInheritingStyles.cpp b/layout/style/CachedInheritingStyles.cpp new file mode 100644 index 0000000000..da6e4d24e6 --- /dev/null +++ b/layout/style/CachedInheritingStyles.cpp @@ -0,0 +1,70 @@ +/* -*- 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 "mozilla/CachedInheritingStyles.h" + +#include "mozilla/ComputedStyle.h" +#include "nsCOMPtr.h" +#include "nsWindowSizes.h" + +namespace mozilla { + +void CachedInheritingStyles::Insert(ComputedStyle* aStyle) { + MOZ_ASSERT(aStyle); + MOZ_ASSERT(aStyle->IsInheritingAnonBox() || + aStyle->IsLazilyCascadedPseudoElement()); + + if (IsEmpty()) { + RefPtr s = aStyle; + mBits = reinterpret_cast(s.forget().take()); + MOZ_ASSERT(!IsEmpty() && !IsIndirect()); + } else if (IsIndirect()) { + AsIndirect()->AppendElement(aStyle); + } else { + IndirectCache* cache = new IndirectCache(); + cache->AppendElement(dont_AddRef(AsDirect())); + cache->AppendElement(aStyle); + mBits = reinterpret_cast(cache) | 1; + MOZ_ASSERT(IsIndirect()); + } +} + +ComputedStyle* CachedInheritingStyles::Lookup(PseudoStyleType aType) const { + MOZ_ASSERT(PseudoStyle::IsPseudoElement(aType) || + PseudoStyle::IsInheritingAnonBox(aType)); + if (IsIndirect()) { + for (auto& style : *AsIndirect()) { + if (style->GetPseudoType() == aType) { + return style; + } + } + + return nullptr; + } + + ComputedStyle* direct = AsDirect(); + return direct && direct->GetPseudoType() == aType ? direct : nullptr; +} + +void CachedInheritingStyles::AddSizeOfIncludingThis(nsWindowSizes& aSizes, + size_t* aCVsSize) const { + if (IsIndirect()) { + for (auto& style : *AsIndirect()) { + if (!aSizes.mState.HaveSeenPtr(style)) { + style->AddSizeOfIncludingThis(aSizes, aCVsSize); + } + } + + return; + } + + ComputedStyle* direct = AsDirect(); + if (direct && !aSizes.mState.HaveSeenPtr(direct)) { + direct->AddSizeOfIncludingThis(aSizes, aCVsSize); + } +} + +} // namespace mozilla diff --git a/layout/style/CachedInheritingStyles.h b/layout/style/CachedInheritingStyles.h new file mode 100644 index 0000000000..cfe86594ca --- /dev/null +++ b/layout/style/CachedInheritingStyles.h @@ -0,0 +1,66 @@ +/* -*- 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_CachedInheritingStyles_h +#define mozilla_CachedInheritingStyles_h + +#include "nsAtom.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" + +class nsWindowSizes; + +namespace mozilla { + +enum class PseudoStyleType : uint8_t; +class ComputedStyle; + +// Cache of anonymous box and lazy pseudo styles that inherit from a given +// style. +// +// To minimize memory footprint, the cache is word-sized with a tagged pointer +// If there is only one entry, it's stored inline. If there are more, they're +// stored in an out-of-line buffer. See bug 1429126 comment 0 and comment 1 for +// the measurements and rationale that influenced the design. +class CachedInheritingStyles { + public: + void Insert(ComputedStyle* aStyle); + ComputedStyle* Lookup(PseudoStyleType) const; + + CachedInheritingStyles() : mBits(0) {} + ~CachedInheritingStyles() { + if (IsIndirect()) { + delete AsIndirect(); + } else if (!IsEmpty()) { + RefPtr ref = dont_AddRef(AsDirect()); + } + } + + void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const; + + private: + // See bug 1429126 comment 1 for the choice of four here. + typedef AutoTArray, 4> IndirectCache; + + bool IsEmpty() const { return !mBits; } + bool IsIndirect() const { return (mBits & 1); } + + ComputedStyle* AsDirect() const { + MOZ_ASSERT(!IsIndirect()); + return reinterpret_cast(mBits); + } + + IndirectCache* AsIndirect() const { + MOZ_ASSERT(IsIndirect()); + return reinterpret_cast(mBits & ~1); + } + + uintptr_t mBits; +}; + +} // namespace mozilla + +#endif // mozilla_CachedInheritingStyles_h diff --git a/layout/style/ComputedStyle.cpp b/layout/style/ComputedStyle.cpp new file mode 100644 index 0000000000..1cb291b27b --- /dev/null +++ b/layout/style/ComputedStyle.cpp @@ -0,0 +1,433 @@ +/* -*- 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/. */ + +/* the interface (to internal code) for retrieving computed style data */ + +#include "mozilla/ComputedStyle.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Maybe.h" +#include "mozilla/ToString.h" + +#include "nsCSSAnonBoxes.h" +#include "nsCSSPseudoElements.h" +#include "nsFontMetrics.h" +#include "nsStyleConsts.h" +#include "nsStyleStruct.h" +#include "nsStyleStructInlines.h" +#include "nsString.h" +#include "nsPresContext.h" +#include "nsWindowSizes.h" + +#include "nsCOMPtr.h" + +#include "mozilla/dom/Document.h" +#include "nsPrintfCString.h" +#include "RubyUtils.h" +#include "mozilla/ComputedStyleInlines.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProfilerLabels.h" + +#include "mozilla/ReflowInput.h" +#include "nsLayoutUtils.h" +#include "nsCoord.h" + +// Ensure the binding function declarations in ComputedStyle.h matches +// those in ServoBindings.h. +#include "mozilla/ServoBindings.h" + +namespace mozilla { + +ComputedStyle::ComputedStyle(PseudoStyleType aPseudoType, + ServoComputedDataForgotten aComputedValues) + : mSource(aComputedValues), mPseudoType(aPseudoType) {} + +// If a struct returned nsChangeHint_UpdateContainingBlock, that means that one +// property's influence on whether we're a containing block for abs-pos or +// fixed-pos elements has changed. +// +// However, we only need to return the hint if the overall computation of +// whether we establish a containing block has really changed. +static bool ContainingBlockMayHaveChanged(const ComputedStyle& aOldStyle, + const ComputedStyle& aNewStyle) { + const auto& oldDisp = *aOldStyle.StyleDisplay(); + const auto& newDisp = *aNewStyle.StyleDisplay(); + + if (oldDisp.IsPositionedStyle() != newDisp.IsPositionedStyle()) { + // XXX This check can probably be moved to after the fixedCB check, since + // IsPositionedStyle() is also only relevant for non-svg text frame + // subtrees. + return true; + } + + const bool fixedCB = aOldStyle.IsFixedPosContainingBlockForNonSVGTextFrames(); + if (fixedCB != aNewStyle.IsFixedPosContainingBlockForNonSVGTextFrames()) { + return true; + } + // If we were both before and after a fixed-pos containing-block that means + // that everything else doesn't matter, since all the other conditions are a + // subset of this. + if (fixedCB) { + return false; + } + + // Note that neither of these two following sets of frames + // (transform-supporting and layout-and-paint-supporting frames) is a subset + // of the other, because table frames support contain: layout/paint but not + // transforms (which are instead inherited to the table wrapper), and quite a + // few frame types support transforms but not contain: layout/paint (e.g., + // table rows and row groups, many SVG frames). + if (oldDisp.IsFixedPosContainingBlockForTransformSupportingFrames() != + newDisp.IsFixedPosContainingBlockForTransformSupportingFrames()) { + return true; + } + if (oldDisp + .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() != + newDisp + .IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames()) { + return true; + } + return false; +} + +nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle, + uint32_t* aEqualStructs) const { + AUTO_PROFILER_LABEL_HOT("ComputedStyle::CalcStyleDifference", LAYOUT); + static_assert(StyleStructConstants::kStyleStructCount <= 32, + "aEqualStructs is not big enough"); + + *aEqualStructs = 0; + + nsChangeHint hint = nsChangeHint(0); + // We must always ensure that we populate the structs on the new style + // context that are filled in on the old context, so that if we get + // two style changes in succession, the second of which causes a real + // style change, the PeekStyleData doesn't return null (implying that + // nobody ever looked at that struct's data). In other words, we + // can't skip later structs if we get a big change up front, because + // we could later get a small change in one of those structs that we + // don't want to miss. + + DebugOnly structsFound = 0; + + DebugOnly styleStructCount = 0; + + // Servo's optimization to stop the cascade when there are no style changes + // that children need to be recascade for relies on comparing all of the + // structs, not just those that are returned from PeekStyleData, although + // if PeekStyleData does return null we could avoid to accumulate any change + // hints for those structs. + // + // FIXME(emilio): Reintroduce that optimization either for all kind of structs + // after bug 1368290 with a weak parent pointer from text, or just for reset + // structs. +#define STYLE_STRUCT_BIT(name_) \ + StyleStructConstants::BitFor(StyleStructID::name_) + +#define EXPAND(...) __VA_ARGS__ +#define DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, extra_args_) \ + PR_BEGIN_MACRO \ + const nsStyle##struct_* this##struct_ = Style##struct_(); \ + structsFound |= STYLE_STRUCT_BIT(struct_); \ + \ + const nsStyle##struct_* other##struct_ = aNewStyle.Style##struct_(); \ + if (this##struct_ == other##struct_) { \ + /* The very same struct, so we know that there will be no */ \ + /* differences. */ \ + *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \ + } else { \ + nsChangeHint difference = \ + this##struct_->CalcDifference(*other##struct_ EXPAND extra_args_); \ + hint |= difference; \ + if (!difference) { \ + *aEqualStructs |= STYLE_STRUCT_BIT(struct_); \ + } \ + } \ + styleStructCount++; \ + PR_END_MACRO +#define DO_STRUCT_DIFFERENCE(struct_) \ + DO_STRUCT_DIFFERENCE_WITH_ARGS(struct_, ()) + + // FIXME: The order of these DO_STRUCT_DIFFERENCE calls is no longer + // significant. With a small amount of effort, we could replace them with a + // #include "nsStyleStructList.h". + DO_STRUCT_DIFFERENCE_WITH_ARGS(Display, (, *this)); + DO_STRUCT_DIFFERENCE(XUL); + DO_STRUCT_DIFFERENCE(Column); + DO_STRUCT_DIFFERENCE(Content); + DO_STRUCT_DIFFERENCE(UI); + DO_STRUCT_DIFFERENCE(Visibility); + DO_STRUCT_DIFFERENCE(Outline); + DO_STRUCT_DIFFERENCE(TableBorder); + DO_STRUCT_DIFFERENCE(Table); + DO_STRUCT_DIFFERENCE(UIReset); + DO_STRUCT_DIFFERENCE(Text); + DO_STRUCT_DIFFERENCE_WITH_ARGS(List, (, *this)); + DO_STRUCT_DIFFERENCE(SVGReset); + DO_STRUCT_DIFFERENCE(SVG); + DO_STRUCT_DIFFERENCE_WITH_ARGS(Position, (, *this)); + DO_STRUCT_DIFFERENCE(Font); + DO_STRUCT_DIFFERENCE(Margin); + DO_STRUCT_DIFFERENCE(Padding); + DO_STRUCT_DIFFERENCE(Border); + DO_STRUCT_DIFFERENCE(TextReset); + DO_STRUCT_DIFFERENCE(Effects); + DO_STRUCT_DIFFERENCE(Background); + DO_STRUCT_DIFFERENCE(Page); + +#undef DO_STRUCT_DIFFERENCE +#undef DO_STRUCT_DIFFERENCE_WITH_ARGS +#undef EXPAND + + MOZ_ASSERT(styleStructCount == StyleStructConstants::kStyleStructCount, + "missing a call to DO_STRUCT_DIFFERENCE"); + + // Note that we do not check whether this->RelevantLinkVisited() != + // aNewContext->RelevantLinkVisited(); we don't need to since + // nsCSSFrameConstructor::DoContentStateChanged always adds + // nsChangeHint_RepaintFrame for ElementState::VISITED changes (and + // needs to, since HasStateDependentStyle probably doesn't work right + // for ElementState::VISITED). Hopefully this doesn't actually + // expose whether links are visited to performance tests since all + // link coloring happens asynchronously at a time when it's hard for + // the page to measure. + // However, we do need to compute the larger of the changes that can + // happen depending on whether the link is visited or unvisited, since + // doing only the one that's currently appropriate would expose which + // links are in history to easy performance measurement. Therefore, + // here, we add nsChangeHint_RepaintFrame hints (the maximum for + // things that can depend on :visited) for the properties on which we + // call GetVisitedDependentColor. + const ComputedStyle* thisVis = GetStyleIfVisited(); + const ComputedStyle* otherVis = aNewStyle.GetStyleIfVisited(); + if (!thisVis != !otherVis) { + // One style has a style-if-visited and the other doesn't. + // Presume a difference. +#define STYLE_STRUCT(name_, fields_) *aEqualStructs &= ~STYLE_STRUCT_BIT(name_); +#include "nsCSSVisitedDependentPropList.h" +#undef STYLE_STRUCT + hint |= nsChangeHint_RepaintFrame; + } else if (thisVis) { + // Both styles have a style-if-visited. + bool change = false; + + // NB: Calling Peek on |this|, not |thisVis|, since callers may look + // at a struct on |this| without looking at the same struct on + // |thisVis| (including this function if we skip one of these checks + // due to change being true already or due to the old style not having a + // style-if-visited), but not the other way around. +#define STYLE_FIELD(name_) thisVisStruct->name_ != otherVisStruct->name_ +#define STYLE_STRUCT(name_, fields_) \ + { \ + const nsStyle##name_* thisVisStruct = thisVis->Style##name_(); \ + const nsStyle##name_* otherVisStruct = otherVis->Style##name_(); \ + if (MOZ_FOR_EACH_SEPARATED(STYLE_FIELD, (||), (), fields_)) { \ + *aEqualStructs &= ~STYLE_STRUCT_BIT(name_); \ + change = true; \ + } \ + } +#include "nsCSSVisitedDependentPropList.h" +#undef STYLE_STRUCT +#undef STYLE_FIELD +#undef STYLE_STRUCT_BIT + + if (change) { + hint |= nsChangeHint_RepaintFrame; + } + } + + if (hint & nsChangeHint_UpdateContainingBlock) { + if (!ContainingBlockMayHaveChanged(*this, aNewStyle)) { + // While some styles that cause the frame to be a containing block + // has changed, the overall result cannot have changed (no matter + // what the frame type is). + hint &= ~nsChangeHint_UpdateContainingBlock; + } + } + + if (HasAuthorSpecifiedBorderOrBackground() != + aNewStyle.HasAuthorSpecifiedBorderOrBackground()) { + const StyleAppearance appearance = StyleDisplay()->EffectiveAppearance(); + if (appearance != StyleAppearance::None && + nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming( + appearance)) { + // A background-specified change may cause padding to change, so we may + // need to reflow. We use the same hint here as we do for "appearance" + // changes. + hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; + } + } + + MOZ_ASSERT(NS_IsHintSubset(hint, nsChangeHint_AllHints), + "Added a new hint without bumping AllHints?"); + return hint & ~nsChangeHint_NeutralChange; +} + +#ifdef DEBUG +void ComputedStyle::List(FILE* out, int32_t aIndent) { + nsAutoCString str; + // Indent + int32_t ix; + for (ix = aIndent; --ix >= 0;) { + str.AppendLiteral(" "); + } + str.Append(nsPrintfCString("%p(%d) parent=%p ", (void*)this, 0, nullptr)); + if (mPseudoType != PseudoStyleType::NotPseudo) { + str.Append(nsPrintfCString("%s ", ToString(mPseudoType).c_str())); + } + + fprintf_stderr(out, "%s{ServoComputedData}\n", str.get()); +} +#endif + +template +static nscolor GetVisitedDependentColorInternal(const ComputedStyle& aStyle, + Func aColorFunc) { + nscolor colors[2]; + colors[0] = aColorFunc(aStyle); + if (const ComputedStyle* visitedStyle = aStyle.GetStyleIfVisited()) { + colors[1] = aColorFunc(*visitedStyle); + return ComputedStyle::CombineVisitedColors(colors, + aStyle.RelevantLinkVisited()); + } + return colors[0]; +} + +static nscolor ExtractColor(const ComputedStyle& aStyle, + const StyleAbsoluteColor& aColor) { + return aColor.ToColor(); +} + +static nscolor ExtractColor(const ComputedStyle& aStyle, + const StyleColor& aColor) { + return aColor.CalcColor(aStyle); +} + +// Currently caret-color, the only property in the list which is a ColorOrAuto, +// always maps auto to currentcolor. +static nscolor ExtractColor(const ComputedStyle& aStyle, + const StyleColorOrAuto& aColor) { + if (aColor.IsAuto()) { + return ExtractColor(aStyle, StyleColor::CurrentColor()); + } + return ExtractColor(aStyle, aColor.AsColor()); +} + +static nscolor ExtractColor(const ComputedStyle& aStyle, + const StyleSVGPaint& aPaintServer) { + return aPaintServer.kind.IsColor() + ? ExtractColor(aStyle, aPaintServer.kind.AsColor()) + : NS_RGBA(0, 0, 0, 0); +} + +#define STYLE_FIELD(struct_, field_) aField == &struct_::field_ || +#define STYLE_STRUCT(name_, fields_) \ + template <> \ + nscolor ComputedStyle::GetVisitedDependentColor( \ + decltype(nsStyle##name_::MOZ_ARG_1 fields_) nsStyle##name_::*aField) \ + const { \ + MOZ_ASSERT(MOZ_FOR_EACH(STYLE_FIELD, (nsStyle##name_, ), fields_) false, \ + "Getting visited-dependent color for a field in nsStyle" #name_ \ + " which is not listed in nsCSSVisitedDependentPropList.h"); \ + return GetVisitedDependentColorInternal( \ + *this, [aField](const ComputedStyle& aStyle) { \ + return ExtractColor(aStyle, aStyle.Style##name_()->*aField); \ + }); \ + } +#include "nsCSSVisitedDependentPropList.h" +#undef STYLE_STRUCT +#undef STYLE_FIELD + +struct ColorIndexSet { + uint8_t colorIndex, alphaIndex; +}; + +static const ColorIndexSet gVisitedIndices[2] = {{0, 0}, {1, 0}}; + +/* static */ +nscolor ComputedStyle::CombineVisitedColors(nscolor* aColors, + bool aLinkIsVisited) { + if (NS_GET_A(aColors[1]) == 0) { + // If the style-if-visited is transparent, then just use the + // unvisited style rather than using the (meaningless) color + // components of the visited style along with a potentially + // non-transparent alpha value. + aLinkIsVisited = false; + } + + // NOTE: We want this code to have as little timing dependence as + // possible on whether this->RelevantLinkVisited() is true. + const ColorIndexSet& set = gVisitedIndices[aLinkIsVisited ? 1 : 0]; + + nscolor colorColor = aColors[set.colorIndex]; + nscolor alphaColor = aColors[set.alphaIndex]; + return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor), + NS_GET_B(colorColor), NS_GET_A(alphaColor)); +} + +#ifdef DEBUG +/* static */ const char* ComputedStyle::StructName(StyleStructID aSID) { + switch (aSID) { +# define STYLE_STRUCT(name_) \ + case StyleStructID::name_: \ + return #name_; +# include "nsStyleStructList.h" +# undef STYLE_STRUCT + default: + return "Unknown"; + } +} + +/* static */ +Maybe ComputedStyle::LookupStruct(const nsACString& aName) { +# define STYLE_STRUCT(name_) \ + if (aName.EqualsLiteral(#name_)) return Some(StyleStructID::name_); +# include "nsStyleStructList.h" +# undef STYLE_STRUCT + return Nothing(); +} +#endif // DEBUG + +ComputedStyle* ComputedStyle::GetCachedLazyPseudoStyle( + PseudoStyleType aPseudo) const { + MOZ_ASSERT(PseudoStyle::IsPseudoElement(aPseudo)); + + if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) { + return nullptr; + } + + return mCachedInheritingStyles.Lookup(aPseudo); +} + +MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf) + +void ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes& aSizes, + size_t* aCVsSize) const { + // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a + // refcount. So we need to measure it with a function that can handle an + // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to + // clearly identify in DMD's output the memory measured here. + *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this); + mSource.AddSizeOfExcludingThis(aSizes); + mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize); +} + +#ifdef DEBUG +bool ComputedStyle::EqualForCachedAnonymousContentStyle( + const ComputedStyle& aOther) const { + // One thing we can't add UA rules to prevent is different -x-lang + // values being inherited in. So we use this FFI function function rather + // than rely on CalcStyleDifference, which can't tell us which specific + // properties have changed. + return Servo_ComputedValues_EqualForCachedAnonymousContentStyle(this, + &aOther); +} + +void ComputedStyle::DumpMatchedRules() const { + Servo_ComputedValues_DumpMatchedRules(this); +} +#endif + +} // namespace mozilla diff --git a/layout/style/ComputedStyle.h b/layout/style/ComputedStyle.h new file mode 100644 index 0000000000..5e3d0d157f --- /dev/null +++ b/layout/style/ComputedStyle.h @@ -0,0 +1,354 @@ +/* -*- 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/. */ + +/* the interface (to internal code) for retrieving computed style data */ + +#ifndef _ComputedStyle_h_ +#define _ComputedStyle_h_ + +#include "mozilla/Assertions.h" +#include "mozilla/CachedInheritingStyles.h" +#include "mozilla/Maybe.h" +#include "mozilla/PseudoStyleType.h" +#include "mozilla/ServoComputedData.h" +#include "mozilla/ServoStyleConsts.h" +#include "nsCSSPseudoElements.h" +#include "nsColor.h" + +#include "nsStyleStructFwd.h" + +enum nsChangeHint : uint32_t; +class nsWindowSizes; + +#define STYLE_STRUCT(name_) struct nsStyle##name_; +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + +extern "C" { +void Gecko_ComputedStyle_Destroy(mozilla::ComputedStyle*); +} + +namespace mozilla { + +enum class StylePointerEvents : uint8_t; +enum class StyleUserSelect : uint8_t; + +namespace dom { +class Document; +} + +/** + * A ComputedStyle represents the computed style data for an element. + * + * The computed style data are stored in a set of reference counted structs + * (see nsStyleStruct.h) that are stored directly on the ComputedStyle. + * + * Style structs are immutable once they have been produced, so when any change + * is made that needs a restyle, we create a new ComputedStyle. + * + * ComputedStyles are reference counted. References are generally held by: + * + * 1. nsIFrame::mComputedStyle, for every frame + * 2. Element::mServoData, for every element not inside a display:none subtree + * 3. nsComputedDOMStyle, when created for elements in display:none subtrees + * 4. media_queries::Device, which holds the initial value of every property + */ + +class ComputedStyle { + using Flag = StyleComputedValueFlags; + + const StyleComputedValueFlags& Flags() const { return mSource.flags; } + + public: + ComputedStyle(PseudoStyleType aPseudoType, + ServoComputedDataForgotten aComputedValues); + + // Returns the computed (not resolved) value of the given property. + void GetComputedPropertyValue(nsCSSPropertyID aId, nsACString& aOut) const { + Servo_GetComputedValue(this, aId, &aOut); + } + + // Return the ComputedStyle whose style data should be used for the R, + // G, and B components of color, background-color, and border-*-color + // if RelevantLinkIsVisited(). + // + // GetPseudo() and GetPseudoType() on this ComputedStyle return the + // same as on |this|, and its depth in the tree (number of GetParent() + // calls until null is returned) is the same as |this|, since its + // parent is either |this|'s parent or |this|'s parent's + // style-if-visited. + // + // Structs on this context should never be examined without also + // examining the corresponding struct on |this|. Doing so will likely + // both (1) lead to a privacy leak and (2) lead to dynamic change bugs + // related to the Peek code in ComputedStyle::CalcStyleDifference. + const ComputedStyle* GetStyleIfVisited() const { + return mSource.visited_style; + } + + bool IsLazilyCascadedPseudoElement() const { + return IsPseudoElement() && + !nsCSSPseudoElements::IsEagerlyCascadedInServo(GetPseudoType()); + } + + PseudoStyleType GetPseudoType() const { return mPseudoType; } + + bool IsPseudoElement() const { + return PseudoStyle::IsPseudoElement(mPseudoType); + } + + bool IsInheritingAnonBox() const { + return PseudoStyle::IsInheritingAnonBox(mPseudoType); + } + + bool IsNonInheritingAnonBox() const { + return PseudoStyle::IsNonInheritingAnonBox(mPseudoType); + } + + bool IsWrapperAnonBox() const { + return PseudoStyle::IsWrapperAnonBox(mPseudoType); + } + + bool IsAnonBox() const { return PseudoStyle::IsAnonBox(mPseudoType); } + + bool IsPseudoOrAnonBox() const { + return mPseudoType != PseudoStyleType::NotPseudo; + } + + // Whether there are author-specified rules for border or background + // properties. + // Only returns something meaningful if the appearance property is not `none`. + bool HasAuthorSpecifiedBorderOrBackground() const { + return bool(Flags() & Flag::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND); + } + + // Whether there are author-specific rules for text color. + bool HasAuthorSpecifiedTextColor() const { + return bool(Flags() & Flag::HAS_AUTHOR_SPECIFIED_TEXT_COLOR); + } + + // Does this ComputedStyle or any of its ancestors have text + // decoration lines? + // Differs from nsStyleTextReset::HasTextDecorationLines, which tests + // only the data for a single context. + bool HasTextDecorationLines() const { + return bool(Flags() & Flag::HAS_TEXT_DECORATION_LINES); + } + + // Whether any line break inside should be suppressed? If this returns + // true, the line should not be broken inside, which means inlines act + // as if nowrap is set,
is suppressed, and blocks are inlinized. + // This bit is propogated to all children of line partitipants. It is + // currently used by ruby to make its content frames unbreakable. + // NOTE: for nsTextFrame, use nsTextFrame::ShouldSuppressLineBreak() + // instead of this method. + bool ShouldSuppressLineBreak() const { + return bool(Flags() & Flag::SHOULD_SUPPRESS_LINEBREAK); + } + + // Is this horizontal-in-vertical (tate-chu-yoko) text? This flag is + // only set on ComputedStyles whose pseudo is nsCSSAnonBoxes::mozText(). + bool IsTextCombined() const { return bool(Flags() & Flag::IS_TEXT_COMBINED); } + + // Whether there's any font metric dependency coming directly from our style. + bool DependsOnSelfFontMetrics() const { + return bool(Flags() & Flag::DEPENDS_ON_SELF_FONT_METRICS); + } + + // Whether there's any font metric dependency coming directly from our parent + // style. + bool DependsOnInheritedFontMetrics() const { + return bool(Flags() & Flag::DEPENDS_ON_INHERITED_FONT_METRICS); + } + + // Does this ComputedStyle represent the style for a pseudo-element or + // inherit data from such a ComputedStyle? Whether this returns true + // is equivalent to whether it or any of its ancestors returns + // non-null for IsPseudoElement(). + bool HasPseudoElementData() const { + return bool(Flags() & Flag::IS_IN_PSEUDO_ELEMENT_SUBTREE); + } + + bool SelfOrAncestorHasContainStyle() const { + return bool(Flags() & Flag::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE); + } + + // Is the only link whose visitedness is allowed to influence the + // style of the node this ComputedStyle is for (which is that element + // or its nearest ancestor that is a link) visited? + bool RelevantLinkVisited() const { + return bool(Flags() & Flag::IS_RELEVANT_LINK_VISITED); + } + + // Whether this style is for the root element of the document. + bool IsRootElementStyle() const { + return bool(Flags() & Flag::IS_ROOT_ELEMENT_STYLE); + } + + bool IsInOpacityZeroSubtree() const { + return bool(Flags() & Flag::IS_IN_OPACITY_ZERO_SUBTREE); + } + + ComputedStyle* GetCachedInheritingAnonBoxStyle( + PseudoStyleType aPseudoType) const { + MOZ_ASSERT(PseudoStyle::IsInheritingAnonBox(aPseudoType)); + return mCachedInheritingStyles.Lookup(aPseudoType); + } + + void SetCachedInheritedAnonBoxStyle(ComputedStyle* aStyle) { + mCachedInheritingStyles.Insert(aStyle); + } + + ComputedStyle* GetCachedLazyPseudoStyle(PseudoStyleType aPseudo) const; + + void SetCachedLazyPseudoStyle(ComputedStyle* aStyle) { + MOZ_ASSERT(aStyle->IsPseudoElement()); + MOZ_ASSERT(!GetCachedLazyPseudoStyle(aStyle->GetPseudoType())); + MOZ_ASSERT(aStyle->IsLazilyCascadedPseudoElement()); + + // Since we're caching lazy pseudo styles on the ComputedValues of the + // originating element, we can assume that we either have the same + // originating element, or that they were at least similar enough to share + // the same ComputedValues, which means that they would match the same + // pseudo rules. This allows us to avoid matching selectors and checking + // the rule node before deciding to share. + // + // The one place this optimization breaks is with pseudo-elements that + // support state (like :hover). So we just avoid sharing in those cases. + if (nsCSSPseudoElements::PseudoElementSupportsUserActionState( + aStyle->GetPseudoType())) { + return; + } + + mCachedInheritingStyles.Insert(aStyle); + } + +#define STYLE_STRUCT(name_) \ + inline const nsStyle##name_* Style##name_() const MOZ_NONNULL_RETURN { \ + return mSource.Style##name_(); \ + } +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + + inline mozilla::StylePointerEvents PointerEvents() const; + inline mozilla::StyleUserSelect UserSelect() const; + + /** + * Returns whether the element is a containing block for its absolutely + * positioned descendants. + * aContextFrame is the frame for which this is the style (or an old style). + */ + inline bool IsAbsPosContainingBlock(const nsIFrame*) const; + + /** + * Returns true when the element is a containing block for its fixed-pos + * descendants. + * aContextFrame is the frame for which this is the style (or an old style). + */ + inline bool IsFixedPosContainingBlock(const nsIFrame*) const; + + /** + * Tests for only the sub-parts of IsFixedPosContainingBlock that apply to: + * - nearly all frames, except those that are in SVG text subtrees. + * - frames that support CSS contain:layout and contain:paint and are not + * in SVG text subtrees. + * - frames that support CSS transforms and are not in SVG text subtrees. + * + * This should be used only when the caller has the style but not the + * frame (i.e., when calculating style changes). + */ + inline bool IsFixedPosContainingBlockForNonSVGTextFrames() const; + + /** + * Compute the style changes needed during restyling when this style + * context is being replaced by aNewContext. (This is nonsymmetric since + * we optimize by skipping comparison for styles that have never been + * requested.) + * + * This method returns a change hint (see nsChangeHint.h). All change + * hints apply to the frame and its later continuations or ib-split + * siblings. Most (all of those except the "NotHandledForDescendants" + * hints) also apply to all descendants. + * + * aEqualStructs must not be null. Into it will be stored a bitfield + * representing which structs were compared to be non-equal. + * + * CSS Variables are not compared here. Instead, the caller is responsible for + * that when needed (basically only for elements). + */ + nsChangeHint CalcStyleDifference(const ComputedStyle& aNewContext, + uint32_t* aEqualStructs) const; + +#ifdef DEBUG + bool EqualForCachedAnonymousContentStyle(const ComputedStyle&) const; +#endif + +#ifdef DEBUG + void DumpMatchedRules() const; +#endif + + /** + * Get a color that depends on link-visitedness using this and + * this->GetStyleIfVisited(). + * + * @param aField A pointer to a member variable in a style struct. + * The member variable and its style struct must have + * been listed in nsCSSVisitedDependentPropList.h. + */ + template + nscolor GetVisitedDependentColor(T S::*aField) const; + + /** + * aColors should be a two element array of nscolor in which the first + * color is the unvisited color and the second is the visited color. + * + * Combine the R, G, and B components of whichever of aColors should + * be used based on aLinkIsVisited with the A component of aColors[0]. + */ + static nscolor CombineVisitedColors(nscolor* aColors, bool aLinkIsVisited); + + /** + * Start image loads for this style. + * + * The Document is used to get a hand on the image loader. The old style is a + * hack for bug 1439285. + */ + inline void StartImageLoads(dom::Document&, + const ComputedStyle* aOldStyle = nullptr); + +#ifdef DEBUG + void List(FILE* out, int32_t aIndent); + static const char* StructName(StyleStructID aSID); + static Maybe LookupStruct(const nsACString& aName); +#endif + + // The |aCVsSize| outparam on this function is where the actual CVs size + // value is added. It's done that way because the callers know which value + // the size should be added to. + void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const; + + StyleWritingMode WritingMode() const { return {mSource.WritingMode().mBits}; } + + const StyleZoom& EffectiveZoom() const { return mSource.effective_zoom; } + + protected: + // Needs to be friend so that it can call the destructor without making it + // public. + friend void ::Gecko_ComputedStyle_Destroy(ComputedStyle*); + + ~ComputedStyle() = default; + + ServoComputedData mSource; + + // A cache of anonymous box and lazy pseudo styles inheriting from this style. + CachedInheritingStyles mCachedInheritingStyles; + + const PseudoStyleType mPseudoType; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/ComputedStyleInlines.h b/layout/style/ComputedStyleInlines.h new file mode 100644 index 0000000000..f9e87c808f --- /dev/null +++ b/layout/style/ComputedStyleInlines.h @@ -0,0 +1,132 @@ +/* -*- 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/. */ + +/* + * Inlined methods for ComputedStyle. Will just redirect to + * GeckoComputedStyle methods when compiled without stylo, but will do + * virtual dispatch (by checking which kind of container it is) + * in stylo mode. + */ + +#ifndef ComputedStyleInlines_h +#define ComputedStyleInlines_h + +#include "mozilla/ComputedStyle.h" + +#include "MainThreadUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/Unused.h" +#include "nsStyleStructInlines.h" + +namespace mozilla { + +namespace detail { + +template +struct HasTriggerImageLoads : public std::false_type {}; + +template +struct HasTriggerImageLoads().TriggerImageLoads( + std::declval(), nullptr))> + : public std::true_type {}; + +template +void TriggerImageLoads(dom::Document& aDocument, const ComputedStyle* aOldStyle, + ComputedStyle* aStyle) { + if constexpr (HasTriggerImageLoads::value) { + auto* old = aOldStyle ? (aOldStyle->*Method)() : nullptr; + auto* current = const_cast((aStyle->*Method)()); + current->TriggerImageLoads(aDocument, old); + } else { + Unused << aOldStyle; + Unused << aStyle; + } +} + +} // namespace detail + +void ComputedStyle::StartImageLoads(dom::Document& aDocument, + const ComputedStyle* aOldStyle) { + MOZ_ASSERT(NS_IsMainThread()); + +#define STYLE_STRUCT(name_) \ + detail::TriggerImageLoads( \ + aDocument, aOldStyle, this); +#include "nsStyleStructList.h" +#undef STYLE_STRUCT +} + +StylePointerEvents ComputedStyle::PointerEvents() const { + if (IsRootElementStyle()) { + // The root frame is not allowed to have pointer-events: none, or else no + // frames could be hit test against and scrolling the viewport would not + // work. + return StylePointerEvents::Auto; + } + const auto& ui = *StyleUI(); + if (ui.IsInert()) { + return StylePointerEvents::None; + } + return ui.ComputedPointerEvents(); +} + +StyleUserSelect ComputedStyle::UserSelect() const { + return StyleUI()->IsInert() ? StyleUserSelect::None + : StyleUIReset()->ComputedUserSelect(); +} + +bool ComputedStyle::IsFixedPosContainingBlockForNonSVGTextFrames() const { + // NOTE: Any CSS properties that influence the output of this function + // should return FIXPOS_CB_NON_SVG for will-change. + if (IsRootElementStyle()) { + return false; + } + + const auto& disp = *StyleDisplay(); + if (disp.mWillChange.bits & mozilla::StyleWillChangeBits::FIXPOS_CB_NON_SVG) { + return true; + } + + const auto& effects = *StyleEffects(); + return effects.HasFilters() || effects.HasBackdropFilters(); +} + +bool ComputedStyle::IsFixedPosContainingBlock( + const nsIFrame* aContextFrame) const { + // NOTE: Any CSS properties that influence the output of this function + // should also handle will-change appropriately. + if (aContextFrame->IsInSVGTextSubtree()) { + return false; + } + if (IsFixedPosContainingBlockForNonSVGTextFrames()) { + return true; + } + const auto& disp = *StyleDisplay(); + if (disp.IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() && + aContextFrame->SupportsContainLayoutAndPaint()) { + return true; + } + if (disp.IsFixedPosContainingBlockForTransformSupportingFrames() && + aContextFrame->SupportsCSSTransforms()) { + return true; + } + return false; +} + +bool ComputedStyle::IsAbsPosContainingBlock( + const nsIFrame* aContextFrame) const { + if (IsFixedPosContainingBlock(aContextFrame)) { + return true; + } + // NOTE: Any CSS properties that influence the output of this function + // should also handle will-change appropriately. + return StyleDisplay()->IsPositionedStyle() && + !aContextFrame->IsInSVGTextSubtree(); +} + +} // namespace mozilla + +#endif // ComputedStyleInlines_h diff --git a/layout/style/CounterStyleManager.cpp b/layout/style/CounterStyleManager.cpp new file mode 100644 index 0000000000..356948de27 --- /dev/null +++ b/layout/style/CounterStyleManager.cpp @@ -0,0 +1,1884 @@ +/* -*- 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 "CounterStyleManager.h" + +#include + +#include "mozilla/ArenaObjectID.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/PresShell.h" +#include "mozilla/Types.h" +#include "mozilla/WritingModes.h" +#include "nsPresContext.h" +#include "nsPresContextInlines.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsTHashtable.h" +#include "nsUnicodeProperties.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoStyleSet.h" + +namespace mozilla { + +using AdditiveSymbol = StyleAdditiveSymbol; + +struct NegativeType { + nsString before, after; +}; + +struct PadType { + int32_t width; + nsString symbol; +}; + +// This limitation will be applied to some systems, and pad descriptor. +// Any initial representation generated by symbolic or additive which is +// longer than this limitation will be dropped. If any pad is longer +// than this, the whole counter text will be dropped as well. +// The spec requires user agents to support at least 60 Unicode code- +// points for counter text. However, this constant only limits the +// length in 16-bit units. So it has to be at least 120, since code- +// points outside the BMP will need 2 16-bit units. +#define LENGTH_LIMIT 150 + +static bool GetCyclicCounterText(CounterValue aOrdinal, nsAString& aResult, + Span aSymbols) { + MOZ_ASSERT(aSymbols.Length() >= 1, "No symbol available for cyclic counter."); + auto n = CounterValue(aSymbols.Length()); + CounterValue index = (aOrdinal - 1) % n; + aResult = aSymbols[index >= 0 ? index : index + n]; + return true; +} + +static bool GetFixedCounterText(CounterValue aOrdinal, nsAString& aResult, + CounterValue aStart, + Span aSymbols) { + CounterValue index = aOrdinal - aStart; + if (index >= 0 && index < CounterValue(aSymbols.Length())) { + aResult = aSymbols[index]; + return true; + } else { + return false; + } +} + +static bool GetSymbolicCounterText(CounterValue aOrdinal, nsAString& aResult, + Span aSymbols) { + MOZ_ASSERT(aSymbols.Length() >= 1, + "No symbol available for symbolic counter."); + MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); + if (aOrdinal == 0) { + return false; + } + + aResult.Truncate(); + auto n = aSymbols.Length(); + const nsString& symbol = aSymbols[(aOrdinal - 1) % n]; + size_t len = (aOrdinal + n - 1) / n; + auto symbolLength = symbol.Length(); + if (symbolLength > 0) { + if (len > LENGTH_LIMIT || symbolLength > LENGTH_LIMIT || + len * symbolLength > LENGTH_LIMIT) { + return false; + } + for (size_t i = 0; i < len; ++i) { + aResult.Append(symbol); + } + } + return true; +} + +static bool GetAlphabeticCounterText(CounterValue aOrdinal, nsAString& aResult, + Span aSymbols) { + MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for alphabetic counter."); + MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); + if (aOrdinal == 0) { + return false; + } + + auto n = aSymbols.Length(); + // The precise length of this array should be + // ceil(log((double) aOrdinal / n * (n - 1) + 1) / log(n)). + // The max length is slightly smaller than which defined below. + AutoTArray::digits> indexes; + while (aOrdinal > 0) { + --aOrdinal; + indexes.AppendElement(aOrdinal % n); + aOrdinal /= n; + } + + aResult.Truncate(); + for (auto i = indexes.Length(); i > 0; --i) { + aResult.Append(aSymbols[indexes[i - 1]]); + } + return true; +} + +static bool GetNumericCounterText(CounterValue aOrdinal, nsAString& aResult, + Span aSymbols) { + MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for numeric counter."); + MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); + + if (aOrdinal == 0) { + aResult = aSymbols[0]; + return true; + } + + auto n = aSymbols.Length(); + AutoTArray::digits> indexes; + while (aOrdinal > 0) { + indexes.AppendElement(aOrdinal % n); + aOrdinal /= n; + } + + aResult.Truncate(); + for (auto i = indexes.Length(); i > 0; --i) { + aResult.Append(aSymbols[indexes[i - 1]]); + } + return true; +} + +static bool GetAdditiveCounterText(CounterValue aOrdinal, nsAString& aResult, + Span aSymbols) { + MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); + + if (aOrdinal == 0) { + const AdditiveSymbol& last = aSymbols[aSymbols.Length() - 1]; + if (last.weight == 0) { + aResult = last.symbol; + return true; + } + return false; + } + + aResult.Truncate(); + size_t length = 0; + for (size_t i = 0, iEnd = aSymbols.Length(); i < iEnd; ++i) { + const AdditiveSymbol& symbol = aSymbols[i]; + if (symbol.weight == 0) { + break; + } + CounterValue times = aOrdinal / symbol.weight; + if (times > 0) { + auto symbolLength = symbol.symbol.Length(); + if (symbolLength > 0) { + length += times * symbolLength; + if (times > LENGTH_LIMIT || symbolLength > LENGTH_LIMIT || + length > LENGTH_LIMIT) { + return false; + } + for (CounterValue j = 0; j < times; ++j) { + aResult.Append(symbol.symbol); + } + } + aOrdinal -= times * symbol.weight; + } + } + return aOrdinal == 0; +} + +static bool DecimalToText(CounterValue aOrdinal, nsAString& aResult) { + aResult.AppendInt(aOrdinal); + return true; +} + +// We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999 +// georgian needs 6 at most +// armenian needs 12 at most +// hebrew may need more... + +#define NUM_BUF_SIZE 34 + +enum CJKIdeographicLang { CHINESE, KOREAN, JAPANESE }; +struct CJKIdeographicData { + char16_t digit[10]; + char16_t unit[3]; + char16_t unit10K[2]; + uint8_t lang; + bool informal; +}; +static const CJKIdeographicData gDataJapaneseInformal = { + {0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x5341, 0x767e, 0x5343}, // unit + {0x4e07, 0x5104}, // unit10K + JAPANESE, // lang + true // informal +}; +static const CJKIdeographicData gDataJapaneseFormal = { + {0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db, 0x4f0d, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x62fe, 0x767e, 0x9621}, // unit + {0x842c, 0x5104}, // unit10K + JAPANESE, // lang + false // informal +}; +static const CJKIdeographicData gDataKoreanHangulFormal = { + {0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, 0xc624, 0xc721, 0xce60, 0xd314, + 0xad6c}, // digit + {0xc2ed, 0xbc31, 0xcc9c}, // unit + {0xb9cc, 0xc5b5}, // unit10K + KOREAN, // lang + false // informal +}; +static const CJKIdeographicData gDataKoreanHanjaInformal = { + {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x5341, 0x767e, 0x5343}, // unit + {0x842c, 0x5104}, // unit10K + KOREAN, // lang + true // informal +}; +static const CJKIdeographicData gDataKoreanHanjaFormal = { + {0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x62fe, 0x767e, 0x4edf}, // unit + {0x842c, 0x5104}, // unit10K + KOREAN, // lang + false // informal +}; +static const CJKIdeographicData gDataSimpChineseInformal = { + {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x5341, 0x767e, 0x5343}, // unit + {0x4e07, 0x4ebf}, // unit10K + CHINESE, // lang + true // informal +}; +static const CJKIdeographicData gDataSimpChineseFormal = { + {0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086, 0x4f0d, 0x9646, 0x67d2, 0x634c, + 0x7396}, // digit + {0x62fe, 0x4f70, 0x4edf}, // unit + {0x4e07, 0x4ebf}, // unit10K + CHINESE, // lang + false // informal +}; +static const CJKIdeographicData gDataTradChineseInformal = { + {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, + 0x4e5d}, // digit + {0x5341, 0x767e, 0x5343}, // unit + {0x842c, 0x5104}, // unit10K + CHINESE, // lang + true // informal +}; +static const CJKIdeographicData gDataTradChineseFormal = { + {0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086, 0x4f0d, 0x9678, 0x67d2, 0x634c, + 0x7396}, // digit + {0x62fe, 0x4f70, 0x4edf}, // unit + {0x842c, 0x5104}, // unit10K + CHINESE, // lang + false // informal +}; + +static bool CJKIdeographicToText(CounterValue aOrdinal, nsAString& aResult, + const CJKIdeographicData& data) { + NS_ASSERTION(aOrdinal >= 0, "Only accept non-negative ordinal"); + char16_t buf[NUM_BUF_SIZE]; + int32_t idx = NUM_BUF_SIZE; + int32_t pos = 0; + bool needZero = (aOrdinal == 0); + int32_t unitidx = 0, unit10Kidx = 0; + do { + unitidx = pos % 4; + if (unitidx == 0) { + unit10Kidx = pos / 4; + } + auto cur = static_cast>(aOrdinal) % 10; + if (cur == 0) { + if (needZero) { + needZero = false; + buf[--idx] = data.digit[0]; + } + } else { + if (data.lang == CHINESE) { + needZero = true; + } + if (unit10Kidx != 0) { + if (data.lang == KOREAN && idx != NUM_BUF_SIZE) { + buf[--idx] = ' '; + } + buf[--idx] = data.unit10K[unit10Kidx - 1]; + } + if (unitidx != 0) { + buf[--idx] = data.unit[unitidx - 1]; + } + if (cur != 1) { + buf[--idx] = data.digit[cur]; + } else { + bool needOne = true; + if (data.informal) { + switch (data.lang) { + case CHINESE: + if (unitidx == 1 && + (aOrdinal == 1 || (pos > 4 && aOrdinal % 1000 == 1))) { + needOne = false; + } + break; + case JAPANESE: + if (unitidx > 0 && + (unitidx != 3 || (pos == 3 && aOrdinal == 1))) { + needOne = false; + } + break; + case KOREAN: + if (unitidx > 0 || (pos == 4 && (aOrdinal % 1000) == 1)) { + needOne = false; + } + break; + } + } + if (needOne) { + buf[--idx] = data.digit[1]; + } + } + unit10Kidx = 0; + } + aOrdinal /= 10; + pos++; + } while (aOrdinal > 0); + aResult.Assign(buf + idx, NUM_BUF_SIZE - idx); + return true; +} + +#define HEBREW_GERESH 0x05F3 +static const char16_t gHebrewDigit[22] = { + // 1 2 3 4 5 6 7 8 9 + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, + // 10 20 30 40 50 60 70 80 90 + 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6, + // 100 200 300 400 + 0x05E7, 0x05E8, 0x05E9, 0x05EA}; + +static bool HebrewToText(CounterValue aOrdinal, nsAString& aResult) { + if (aOrdinal < 1 || aOrdinal > 999999) { + return false; + } + + bool outputSep = false; + nsAutoString allText, thousandsGroup; + do { + thousandsGroup.Truncate(); + int32_t n3 = aOrdinal % 1000; + // Process digit for 100 - 900 + for (int32_t n1 = 400; n1 > 0;) { + if (n3 >= n1) { + n3 -= n1; + thousandsGroup.Append(gHebrewDigit[(n1 / 100) - 1 + 18]); + } else { + n1 -= 100; + } // if + } // for + + // Process digit for 10 - 90 + int32_t n2; + if (n3 >= 10) { + // Special process for 15 and 16 + if ((15 == n3) || (16 == n3)) { + // Special rule for religious reason... + // 15 is represented by 9 and 6, not 10 and 5 + // 16 is represented by 9 and 7, not 10 and 6 + n2 = 9; + thousandsGroup.Append(gHebrewDigit[n2 - 1]); + } else { + n2 = n3 - (n3 % 10); + thousandsGroup.Append(gHebrewDigit[(n2 / 10) - 1 + 9]); + } // if + n3 -= n2; + } // if + + // Process digit for 1 - 9 + if (n3 > 0) thousandsGroup.Append(gHebrewDigit[n3 - 1]); + if (outputSep) thousandsGroup.Append((char16_t)HEBREW_GERESH); + if (allText.IsEmpty()) + allText = thousandsGroup; + else + allText = thousandsGroup + allText; + aOrdinal /= 1000; + outputSep = true; + } while (aOrdinal >= 1); + + aResult = allText; + return true; +} + +// Convert ordinal to Ethiopic numeric representation. +// The detail is available at http://www.ethiopic.org/Numerals/ +// The algorithm used here is based on the pseudo-code put up there by +// Daniel Yacob . +// Another reference is Unicode 3.0 standard section 11.1. +#define ETHIOPIC_ONE 0x1369 +#define ETHIOPIC_TEN 0x1372 +#define ETHIOPIC_HUNDRED 0x137B +#define ETHIOPIC_TEN_THOUSAND 0x137C + +static bool EthiopicToText(CounterValue aOrdinal, nsAString& aResult) { + if (aOrdinal < 1) { + return false; + } + + nsAutoString asciiNumberString; // decimal string representation of ordinal + DecimalToText(aOrdinal, asciiNumberString); + uint8_t asciiStringLength = asciiNumberString.Length(); + + // If number length is odd, add a leading "0" + // the leading "0" preconditions the string to always have the + // leading tens place populated, this avoids a check within the loop. + // If we didn't add the leading "0", decrement asciiStringLength so + // it will be equivalent to a zero-based index in both cases. + if (asciiStringLength & 1) { + asciiNumberString.InsertLiteral(u"0", 0); + } else { + asciiStringLength--; + } + + aResult.Truncate(); + // Iterate from the highest digits to lowest + // indexFromLeft indexes digits (0 = most significant) + // groupIndexFromRight indexes pairs of digits (0 = least significant) + for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1; + indexFromLeft <= asciiStringLength; + indexFromLeft += 2, groupIndexFromRight--) { + uint8_t tensValue = asciiNumberString.CharAt(indexFromLeft) & 0x0F; + uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F; + uint8_t groupValue = tensValue * 10 + unitsValue; + + bool oddGroup = (groupIndexFromRight & 1); + + // we want to clear ETHIOPIC_ONE when it is superfluous + if (aOrdinal > 1 && groupValue == 1 && // one without a leading ten + (oddGroup || + indexFromLeft == 0)) { // preceding (100) or leading the sequence + unitsValue = 0; + } + + // put it all together... + if (tensValue) { + // map onto Ethiopic "tens": + aResult.Append((char16_t)(tensValue + ETHIOPIC_TEN - 1)); + } + if (unitsValue) { + // map onto Ethiopic "units": + aResult.Append((char16_t)(unitsValue + ETHIOPIC_ONE - 1)); + } + // Add a separator for all even groups except the last, + // and for odd groups with non-zero value. + if (oddGroup) { + if (groupValue) { + aResult.Append((char16_t)ETHIOPIC_HUNDRED); + } + } else { + if (groupIndexFromRight) { + aResult.Append((char16_t)ETHIOPIC_TEN_THOUSAND); + } + } + } + return true; +} + +static SpeakAs GetDefaultSpeakAsForSystem(StyleCounterSystem aSystem) { + MOZ_ASSERT(aSystem != StyleCounterSystem::Extends, + "Extends system does not have static default speak-as"); + switch (aSystem) { + case StyleCounterSystem::Alphabetic: + return SpeakAs::Spellout; + case StyleCounterSystem::Cyclic: + return SpeakAs::Bullets; + default: + return SpeakAs::Numbers; + } +} + +static bool SystemUsesNegativeSign(StyleCounterSystem aSystem) { + MOZ_ASSERT(aSystem != StyleCounterSystem::Extends, + "Cannot check this for extending style"); + switch (aSystem) { + case StyleCounterSystem::Symbolic: + case StyleCounterSystem::Alphabetic: + case StyleCounterSystem::Numeric: + case StyleCounterSystem::Additive: + return true; + default: + return false; + } +} + +class BuiltinCounterStyle : public CounterStyle { + public: + constexpr BuiltinCounterStyle(ListStyle aStyle, nsStaticAtom* aName) + : CounterStyle(aStyle), mName(aName) {} + + nsStaticAtom* GetStyleName() const { return mName; } + + virtual void GetPrefix(nsAString& aResult) override; + virtual void GetSuffix(nsAString& aResult) override; + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsBullet) override; + virtual bool IsBullet() override; + + virtual void GetNegative(NegativeType& aResult) override; + virtual bool IsOrdinalInRange(CounterValue aOrdinal) override; + virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; + virtual void GetPad(PadType& aResult) override; + virtual CounterStyle* GetFallback() override; + virtual SpeakAs GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) override; + + protected: + constexpr BuiltinCounterStyle(const BuiltinCounterStyle& aOther) + : CounterStyle(aOther.mStyle), mName(aOther.mName) {} + + private: + nsStaticAtom* mName; +}; + +/* virtual */ +void BuiltinCounterStyle::GetPrefix(nsAString& aResult) { aResult.Truncate(); } + +/* virtual */ +void BuiltinCounterStyle::GetSuffix(nsAString& aResult) { + switch (mStyle) { + case ListStyle::None: + aResult.Truncate(); + break; + + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + case ListStyle::EthiopicNumeric: + aResult = ' '; + break; + + case ListStyle::TradChineseInformal: + case ListStyle::TradChineseFormal: + case ListStyle::SimpChineseInformal: + case ListStyle::SimpChineseFormal: + case ListStyle::JapaneseInformal: + case ListStyle::JapaneseFormal: + aResult = 0x3001; + break; + + case ListStyle::KoreanHangulFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHanjaFormal: + aResult.AssignLiteral(u", "); + break; + + default: + aResult.AssignLiteral(u". "); + break; + } +} + +static const char16_t kDiscCharacter = 0x2022; +static const char16_t kCircleCharacter = 0x25e6; +static const char16_t kSquareCharacter = 0x25aa; +static const char16_t kRightPointingCharacter = 0x25b8; +static const char16_t kLeftPointingCharacter = 0x25c2; +static const char16_t kDownPointingCharacter = 0x25be; + +/* virtual */ +void BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsBullet) { + switch (mStyle) { + case ListStyle::None: + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: { + // Same as the initial representation + bool isRTL; + GetInitialCounterText(aOrdinal, aWritingMode, aResult, isRTL); + aIsBullet = true; + break; + } + default: + CounterStyle::GetSpokenCounterText(aOrdinal, aWritingMode, aResult, + aIsBullet); + break; + } +} + +/* virtual */ +bool BuiltinCounterStyle::IsBullet() { + switch (mStyle) { + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + return true; + default: + return false; + } +} + +static const char16_t gJapaneseNegative[] = {0x30de, 0x30a4, 0x30ca, 0x30b9, + 0x0000}; +static const char16_t gKoreanNegative[] = {0xb9c8, 0xc774, 0xb108, + 0xc2a4, 0x0020, 0x0000}; +static const char16_t gSimpChineseNegative[] = {0x8d1f, 0x0000}; +static const char16_t gTradChineseNegative[] = {0x8ca0, 0x0000}; + +/* virtual */ +void BuiltinCounterStyle::GetNegative(NegativeType& aResult) { + switch (mStyle) { + case ListStyle::JapaneseFormal: + case ListStyle::JapaneseInformal: + aResult.before = gJapaneseNegative; + break; + + case ListStyle::KoreanHangulFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHanjaFormal: + aResult.before = gKoreanNegative; + break; + + case ListStyle::SimpChineseFormal: + case ListStyle::SimpChineseInformal: + aResult.before = gSimpChineseNegative; + break; + + case ListStyle::TradChineseFormal: + case ListStyle::TradChineseInformal: + aResult.before = gTradChineseNegative; + break; + + default: + aResult.before.AssignLiteral(u"-"); + } + aResult.after.Truncate(); +} + +/* virtual */ +bool BuiltinCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { + switch (mStyle) { + default: + // cyclic + case ListStyle::None: + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + // use DecimalToText + case ListStyle::Decimal: + // use CJKIdeographicToText + case ListStyle::JapaneseFormal: + case ListStyle::JapaneseInformal: + case ListStyle::KoreanHanjaFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHangulFormal: + case ListStyle::TradChineseFormal: + case ListStyle::TradChineseInformal: + case ListStyle::SimpChineseFormal: + case ListStyle::SimpChineseInformal: + return true; + + // use EthiopicToText + case ListStyle::EthiopicNumeric: + return aOrdinal >= 1; + + // use HebrewToText + case ListStyle::Hebrew: + return aOrdinal >= 1 && aOrdinal <= 999999; + } +} + +/* virtual */ +bool BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { + switch (mStyle) { + // cyclic: + case ListStyle::None: + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + // numeric: + case ListStyle::Decimal: + return true; + + // additive: + case ListStyle::Hebrew: + return aOrdinal >= 0; + + // complex predefined: + case ListStyle::JapaneseFormal: + case ListStyle::JapaneseInformal: + case ListStyle::KoreanHanjaFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHangulFormal: + case ListStyle::TradChineseFormal: + case ListStyle::TradChineseInformal: + case ListStyle::SimpChineseFormal: + case ListStyle::SimpChineseInformal: + case ListStyle::EthiopicNumeric: + return IsOrdinalInRange(aOrdinal); + + default: + MOZ_ASSERT_UNREACHABLE("Unknown counter style"); + return false; + } +} + +/* virtual */ +void BuiltinCounterStyle::GetPad(PadType& aResult) { + aResult.width = 0; + aResult.symbol.Truncate(); +} + +/* virtual */ +CounterStyle* BuiltinCounterStyle::GetFallback() { + // Fallback of dependent builtin counter styles are handled in class + // DependentBuiltinCounterStyle. + return CounterStyleManager::GetDecimalStyle(); +} + +/* virtual */ +SpeakAs BuiltinCounterStyle::GetSpeakAs() { + switch (mStyle) { + case ListStyle::None: + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + return SpeakAs::Bullets; + default: + return SpeakAs::Numbers; + } +} + +/* virtual */ +bool BuiltinCounterStyle::UseNegativeSign() { + switch (mStyle) { + case ListStyle::None: + case ListStyle::Disc: + case ListStyle::Circle: + case ListStyle::Square: + case ListStyle::DisclosureClosed: + case ListStyle::DisclosureOpen: + return false; + default: + return true; + } +} + +/* virtual */ +bool BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsRTL) { + aIsRTL = false; + switch (mStyle) { + // used by counters & extends counter-style code only + // XXX We really need to do this the same way we do list bullets. + case ListStyle::None: + aResult.Truncate(); + return true; + case ListStyle::Disc: + aResult.Assign(kDiscCharacter); + return true; + case ListStyle::Circle: + aResult.Assign(kCircleCharacter); + return true; + case ListStyle::Square: + aResult.Assign(kSquareCharacter); + return true; + case ListStyle::DisclosureClosed: + if (aWritingMode.IsVertical()) { + aResult.Assign(kDownPointingCharacter); + } else if (aWritingMode.IsBidiLTR()) { + aResult.Assign(kRightPointingCharacter); + } else { + aResult.Assign(kLeftPointingCharacter); + } + return true; + case ListStyle::DisclosureOpen: + if (!aWritingMode.IsVertical()) { + aResult.Assign(kDownPointingCharacter); + } else if (aWritingMode.IsVerticalLR()) { + aResult.Assign(kRightPointingCharacter); + } else { + aResult.Assign(kLeftPointingCharacter); + } + return true; + + case ListStyle::Decimal: + return DecimalToText(aOrdinal, aResult); + + case ListStyle::TradChineseInformal: + return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseInformal); + case ListStyle::TradChineseFormal: + return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseFormal); + case ListStyle::SimpChineseInformal: + return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseInformal); + case ListStyle::SimpChineseFormal: + return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseFormal); + case ListStyle::JapaneseInformal: + return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseInformal); + case ListStyle::JapaneseFormal: + return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseFormal); + case ListStyle::KoreanHangulFormal: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHangulFormal); + case ListStyle::KoreanHanjaInformal: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaInformal); + case ListStyle::KoreanHanjaFormal: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaFormal); + + case ListStyle::Hebrew: + aIsRTL = true; + return HebrewToText(aOrdinal, aResult); + + case ListStyle::EthiopicNumeric: + return EthiopicToText(aOrdinal, aResult); + + default: + MOZ_ASSERT_UNREACHABLE("Unknown builtin counter style"); + return false; + } +} + +static constexpr BuiltinCounterStyle gBuiltinStyleTable[] = { +#define BUILTIN_COUNTER_STYLE(value_, atom_) \ + {ListStyle::value_, nsGkAtoms::atom_}, +#include "BuiltinCounterStyleList.h" +#undef BUILTIN_COUNTER_STYLE +}; + +#define BUILTIN_COUNTER_STYLE(value_, atom_) \ + static_assert( \ + gBuiltinStyleTable[static_cast(ListStyle::value_)].GetStyle() == \ + ListStyle::value_, \ + "Builtin counter style " #atom_ " has unmatched index and value."); +#include "BuiltinCounterStyleList.h" +#undef BUILTIN_COUNTER_STYLE + +class DependentBuiltinCounterStyle final : public BuiltinCounterStyle { + public: + DependentBuiltinCounterStyle(ListStyle aStyle, CounterStyleManager* aManager) + : BuiltinCounterStyle(gBuiltinStyleTable[static_cast(aStyle)]), + mManager(aManager) { + NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style"); + MOZ_ASSERT(!IsCustomStyle(), "Not a builtin style"); + } + + virtual CounterStyle* GetFallback() override; + + void* operator new(size_t sz, nsPresContext* aPresContext) { + return aPresContext->PresShell()->AllocateByObjectID( + eArenaObjectID_DependentBuiltinCounterStyle, sz); + } + + void Destroy() { + PresShell* presShell = mManager->PresContext()->PresShell(); + this->~DependentBuiltinCounterStyle(); + presShell->FreeByObjectID(eArenaObjectID_DependentBuiltinCounterStyle, + this); + } + + private: + ~DependentBuiltinCounterStyle() = default; + + CounterStyleManager* mManager; +}; + +/* virtual */ +CounterStyle* DependentBuiltinCounterStyle::GetFallback() { + switch (GetStyle()) { + case ListStyle::JapaneseInformal: + case ListStyle::JapaneseFormal: + case ListStyle::KoreanHangulFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHanjaFormal: + case ListStyle::SimpChineseInformal: + case ListStyle::SimpChineseFormal: + case ListStyle::TradChineseInformal: + case ListStyle::TradChineseFormal: + // These styles all have a larger range than cjk-decimal, so the + // only case fallback is accessed is that they are extended. + // Since extending styles will cache the data themselves, we need + // not cache it here. + return mManager->ResolveCounterStyle(nsGkAtoms::cjk_decimal); + default: + MOZ_ASSERT_UNREACHABLE("Not a valid dependent builtin style"); + return BuiltinCounterStyle::GetFallback(); + } +} + +class CustomCounterStyle final : public CounterStyle { + public: + CustomCounterStyle(CounterStyleManager* aManager, + const StyleLockedCounterStyleRule* aRule) + : CounterStyle(ListStyle::Custom), + mManager(aManager), + mRule(aRule), + mRuleGeneration(Servo_CounterStyleRule_GetGeneration(aRule)), + mSystem(Servo_CounterStyleRule_GetSystem(aRule)), + mFlags(0), + mFallback(nullptr), + mSpeakAsCounter(nullptr), + mExtends(nullptr), + mExtendsRoot(nullptr) {} + + // This method will clear all cached data in the style and update the + // generation number of the rule. It should be called when the rule of + // this style is changed. + void ResetCachedData(); + + // This method will reset all cached data which may depend on other + // counter style. It will reset all pointers to other counter styles. + // For counter style extends other, in addition, all fields will be + // reset to uninitialized state. This method should be called when any + // other counter style is added, removed, or changed. + void ResetDependentData(); + + const StyleLockedCounterStyleRule* GetRule() const { return mRule; } + uint32_t GetRuleGeneration() const { return mRuleGeneration; } + + virtual void GetPrefix(nsAString& aResult) override; + virtual void GetSuffix(nsAString& aResult) override; + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsBullet) override; + virtual bool IsBullet() override; + + virtual void GetNegative(NegativeType& aResult) override; + virtual bool IsOrdinalInRange(CounterValue aOrdinal) override; + virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; + virtual void GetPad(PadType& aResult) override; + virtual CounterStyle* GetFallback() override; + virtual SpeakAs GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual void CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, nsAString& aResult, + bool& aIsRTL) override; + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) override; + + bool IsExtendsSystem() { return mSystem == StyleCounterSystem::Extends; } + + void* operator new(size_t sz, nsPresContext* aPresContext) { + return aPresContext->PresShell()->AllocateByObjectID( + eArenaObjectID_CustomCounterStyle, sz); + } + + void Destroy() { + PresShell* presShell = mManager->PresContext()->PresShell(); + this->~CustomCounterStyle(); + presShell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this); + } + + private: + ~CustomCounterStyle() = default; + + Span GetSymbols(); + Span GetAdditiveSymbols(); + + // The speak-as values of counter styles may form a loop, and the + // loops may have complex interaction with the loop formed by + // extending. To solve this problem, the computation of speak-as is + // divided into two phases: + // 1. figure out the raw value, by ComputeRawSpeakAs, and + // 2. eliminate loop, by ComputeSpeakAs. + // See comments before the definitions of these methods for details. + SpeakAs GetSpeakAsAutoValue(); + void ComputeRawSpeakAs(SpeakAs& aSpeakAs, CounterStyle*& aSpeakAsCounter); + CounterStyle* ComputeSpeakAs(); + + CounterStyle* ComputeExtends(); + CounterStyle* GetExtends(); + CounterStyle* GetExtendsRoot(); + + // CounterStyleManager should always overlive any CounterStyle as it + // is owned by nsPresContext, and will be released after all nodes and + // frames are released. + CounterStyleManager* mManager; + + RefPtr mRule; + uint32_t mRuleGeneration; + + StyleCounterSystem mSystem; + // GetSpeakAs will ensure that private member mSpeakAs is initialized before + // used + MOZ_INIT_OUTSIDE_CTOR SpeakAs mSpeakAs; + + enum { + // loop detection + FLAG_EXTENDS_VISITED = 1 << 0, + FLAG_EXTENDS_LOOP = 1 << 1, + FLAG_SPEAKAS_VISITED = 1 << 2, + FLAG_SPEAKAS_LOOP = 1 << 3, + // field status + FLAG_NEGATIVE_INITED = 1 << 4, + FLAG_PREFIX_INITED = 1 << 5, + FLAG_SUFFIX_INITED = 1 << 6, + FLAG_PAD_INITED = 1 << 7, + FLAG_SPEAKAS_INITED = 1 << 8, + }; + uint16_t mFlags; + + // Fields below will be initialized when necessary. + StyleOwnedSlice mSymbols; + StyleOwnedSlice mAdditiveSymbols; + NegativeType mNegative; + nsString mPrefix, mSuffix; + PadType mPad; + + // CounterStyleManager will guarantee that none of the pointers below + // refers to a freed CounterStyle. There are two possible cases where + // the manager will release its reference to a CounterStyle: 1. the + // manager itself is released, 2. a rule is invalidated. In the first + // case, all counter style are removed from the manager, and should + // also have been dereferenced from other objects. All styles will be + // released all together. In the second case, CounterStyleManager:: + // NotifyRuleChanged will guarantee that all pointers will be reset + // before any CounterStyle is released. + + CounterStyle* mFallback; + // This field refers to the last counter in a speak-as chain. + // That counter must not speak as another counter. + CounterStyle* mSpeakAsCounter; + + CounterStyle* mExtends; + // This field refers to the last counter in the extends chain. The + // counter must be either a builtin style or a style whose system is + // not 'extends'. + CounterStyle* mExtendsRoot; +}; + +void CustomCounterStyle::ResetCachedData() { + mSymbols.Clear(); + mAdditiveSymbols.Clear(); + mFlags &= ~(FLAG_NEGATIVE_INITED | FLAG_PREFIX_INITED | FLAG_SUFFIX_INITED | + FLAG_PAD_INITED | FLAG_SPEAKAS_INITED); + mFallback = nullptr; + mSpeakAsCounter = nullptr; + mExtends = nullptr; + mExtendsRoot = nullptr; + mRuleGeneration = Servo_CounterStyleRule_GetGeneration(mRule); +} + +void CustomCounterStyle::ResetDependentData() { + mFlags &= ~FLAG_SPEAKAS_INITED; + mSpeakAsCounter = nullptr; + mFallback = nullptr; + mExtends = nullptr; + mExtendsRoot = nullptr; + if (IsExtendsSystem()) { + mFlags &= ~(FLAG_NEGATIVE_INITED | FLAG_PREFIX_INITED | FLAG_SUFFIX_INITED | + FLAG_PAD_INITED); + } +} + +/* virtual */ +void CustomCounterStyle::GetPrefix(nsAString& aResult) { + if (!(mFlags & FLAG_PREFIX_INITED)) { + mFlags |= FLAG_PREFIX_INITED; + + if (!Servo_CounterStyleRule_GetPrefix(mRule, &mPrefix)) { + if (IsExtendsSystem()) { + GetExtends()->GetPrefix(mPrefix); + } else { + mPrefix.Truncate(); + } + } + } + aResult = mPrefix; +} + +/* virtual */ +void CustomCounterStyle::GetSuffix(nsAString& aResult) { + if (!(mFlags & FLAG_SUFFIX_INITED)) { + mFlags |= FLAG_SUFFIX_INITED; + + if (!Servo_CounterStyleRule_GetSuffix(mRule, &mSuffix)) { + if (IsExtendsSystem()) { + GetExtends()->GetSuffix(mSuffix); + } else { + mSuffix.AssignLiteral(u". "); + } + } + } + aResult = mSuffix; +} + +/* virtual */ +void CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsBullet) { + if (GetSpeakAs() != SpeakAs::Other) { + CounterStyle::GetSpokenCounterText(aOrdinal, aWritingMode, aResult, + aIsBullet); + } else { + MOZ_ASSERT(mSpeakAsCounter, + "mSpeakAsCounter should have been initialized."); + mSpeakAsCounter->GetSpokenCounterText(aOrdinal, aWritingMode, aResult, + aIsBullet); + } +} + +/* virtual */ +bool CustomCounterStyle::IsBullet() { + switch (mSystem) { + case StyleCounterSystem::Cyclic: + // Only use ::-moz-list-bullet for cyclic system + return true; + case StyleCounterSystem::Extends: + return GetExtendsRoot()->IsBullet(); + default: + return false; + } +} + +/* virtual */ +void CustomCounterStyle::GetNegative(NegativeType& aResult) { + if (!(mFlags & FLAG_NEGATIVE_INITED)) { + mFlags |= FLAG_NEGATIVE_INITED; + if (!Servo_CounterStyleRule_GetNegative(mRule, &mNegative.before, + &mNegative.after)) { + if (IsExtendsSystem()) { + GetExtends()->GetNegative(mNegative); + } else { + mNegative.before.AssignLiteral(u"-"); + mNegative.after.Truncate(); + } + } + } + aResult = mNegative; +} + +/* virtual */ +bool CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { + auto inRange = Servo_CounterStyleRule_IsInRange(mRule, aOrdinal); + switch (inRange) { + case StyleIsOrdinalInRange::InRange: + return true; + case StyleIsOrdinalInRange::NotInRange: + return false; + case StyleIsOrdinalInRange::NoOrdinalSpecified: + if (IsExtendsSystem()) { + return GetExtends()->IsOrdinalInRange(aOrdinal); + } + break; + case StyleIsOrdinalInRange::Auto: + break; + default: + MOZ_ASSERT_UNREACHABLE("Unkown result from IsInRange?"); + } + return IsOrdinalInAutoRange(aOrdinal); +} + +/* virtual */ +bool CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { + switch (mSystem) { + case StyleCounterSystem::Cyclic: + case StyleCounterSystem::Numeric: + case StyleCounterSystem::Fixed: + return true; + case StyleCounterSystem::Alphabetic: + case StyleCounterSystem::Symbolic: + return aOrdinal >= 1; + case StyleCounterSystem::Additive: + return aOrdinal >= 0; + case StyleCounterSystem::Extends: + return GetExtendsRoot()->IsOrdinalInAutoRange(aOrdinal); + default: + MOZ_ASSERT_UNREACHABLE("Invalid system for computing auto value."); + return false; + } +} + +/* virtual */ +void CustomCounterStyle::GetPad(PadType& aResult) { + if (!(mFlags & FLAG_PAD_INITED)) { + mFlags |= FLAG_PAD_INITED; + if (!Servo_CounterStyleRule_GetPad(mRule, &mPad.width, &mPad.symbol)) { + if (IsExtendsSystem()) { + GetExtends()->GetPad(mPad); + } else { + mPad.width = 0; + mPad.symbol.Truncate(); + } + } + } + aResult = mPad; +} + +/* virtual */ +CounterStyle* CustomCounterStyle::GetFallback() { + if (!mFallback) { + mFallback = CounterStyleManager::GetDecimalStyle(); + if (nsAtom* fallback = Servo_CounterStyleRule_GetFallback(mRule)) { + mFallback = mManager->ResolveCounterStyle(fallback); + } else if (IsExtendsSystem()) { + mFallback = GetExtends()->GetFallback(); + } + } + return mFallback; +} + +/* virtual */ +SpeakAs CustomCounterStyle::GetSpeakAs() { + if (!(mFlags & FLAG_SPEAKAS_INITED)) { + ComputeSpeakAs(); + } + return mSpeakAs; +} + +/* virtual */ +bool CustomCounterStyle::UseNegativeSign() { + if (mSystem == StyleCounterSystem::Extends) { + return GetExtendsRoot()->UseNegativeSign(); + } + return SystemUsesNegativeSign(mSystem); +} + +/* virtual */ +void CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) { + CounterStyle* fallback = GetFallback(); + // If it recursively falls back to this counter style again, + // it will then fallback to decimal to break the loop. + mFallback = CounterStyleManager::GetDecimalStyle(); + fallback->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); + mFallback = fallback; +} + +/* virtual */ +bool CustomCounterStyle::GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsRTL) { + switch (mSystem) { + case StyleCounterSystem::Cyclic: + return GetCyclicCounterText(aOrdinal, aResult, GetSymbols()); + case StyleCounterSystem::Fixed: { + int32_t start = Servo_CounterStyleRule_GetFixedFirstValue(mRule); + return GetFixedCounterText(aOrdinal, aResult, start, GetSymbols()); + } + case StyleCounterSystem::Symbolic: + return GetSymbolicCounterText(aOrdinal, aResult, GetSymbols()); + case StyleCounterSystem::Alphabetic: + return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols()); + case StyleCounterSystem::Numeric: + return GetNumericCounterText(aOrdinal, aResult, GetSymbols()); + case StyleCounterSystem::Additive: + return GetAdditiveCounterText(aOrdinal, aResult, GetAdditiveSymbols()); + case StyleCounterSystem::Extends: + return GetExtendsRoot()->GetInitialCounterText(aOrdinal, aWritingMode, + aResult, aIsRTL); + default: + MOZ_ASSERT_UNREACHABLE("Invalid system."); + return false; + } +} + +Span CustomCounterStyle::GetSymbols() { + if (mSymbols.IsEmpty()) { + Servo_CounterStyleRule_GetSymbols(mRule, &mSymbols); + } + return mSymbols.AsSpan(); +} + +Span CustomCounterStyle::GetAdditiveSymbols() { + if (mAdditiveSymbols.IsEmpty()) { + Servo_CounterStyleRule_GetAdditiveSymbols(mRule, &mAdditiveSymbols); + } + return mAdditiveSymbols.AsSpan(); +} + +// This method is used to provide the computed value for 'auto'. +SpeakAs CustomCounterStyle::GetSpeakAsAutoValue() { + auto system = mSystem; + if (IsExtendsSystem()) { + CounterStyle* root = GetExtendsRoot(); + if (!root->IsCustomStyle()) { + // It is safe to call GetSpeakAs on non-custom style. + return root->GetSpeakAs(); + } + system = static_cast(root)->mSystem; + } + return GetDefaultSpeakAsForSystem(system); +} + +// This method corresponds to the first stage of computation of the +// value of speak-as. It will extract the value from the rule and +// possibly recursively call itself on the extended style to figure +// out the raw value. To keep things clear, this method is designed to +// have no side effects (but functions it calls may still affect other +// fields in the style.) +void CustomCounterStyle::ComputeRawSpeakAs(SpeakAs& aSpeakAs, + CounterStyle*& aSpeakAsCounter) { + NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED), + "ComputeRawSpeakAs is called with speak-as inited."); + + auto speakAs = StyleCounterSpeakAs::None(); + Servo_CounterStyleRule_GetSpeakAs(mRule, &speakAs); + switch (speakAs.tag) { + case StyleCounterSpeakAs::Tag::Auto: + aSpeakAs = GetSpeakAsAutoValue(); + break; + case StyleCounterSpeakAs::Tag::Bullets: + aSpeakAs = SpeakAs::Bullets; + break; + case StyleCounterSpeakAs::Tag::Numbers: + aSpeakAs = SpeakAs::Numbers; + break; + case StyleCounterSpeakAs::Tag::Words: + aSpeakAs = SpeakAs::Words; + break; + case StyleCounterSpeakAs::Tag::Ident: + aSpeakAs = SpeakAs::Other; + aSpeakAsCounter = mManager->ResolveCounterStyle(speakAs.AsIdent()); + break; + case StyleCounterSpeakAs::Tag::None: { + if (!IsExtendsSystem()) { + aSpeakAs = GetSpeakAsAutoValue(); + } else { + CounterStyle* extended = GetExtends(); + if (!extended->IsCustomStyle()) { + // It is safe to call GetSpeakAs on non-custom style. + aSpeakAs = extended->GetSpeakAs(); + } else { + CustomCounterStyle* custom = + static_cast(extended); + if (!(custom->mFlags & FLAG_SPEAKAS_INITED)) { + custom->ComputeRawSpeakAs(aSpeakAs, aSpeakAsCounter); + } else { + aSpeakAs = custom->mSpeakAs; + aSpeakAsCounter = custom->mSpeakAsCounter; + } + } + } + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Invalid speak-as value"); + } +} + +// This method corresponds to the second stage of getting speak-as +// related values. It will recursively figure out the final value of +// mSpeakAs and mSpeakAsCounter. This method returns nullptr if the +// caller is in a loop, and the root counter style in the chain +// otherwise. It use the same loop detection algorithm as +// CustomCounterStyle::ComputeExtends, see comments before that +// method for more details. +CounterStyle* CustomCounterStyle::ComputeSpeakAs() { + if (mFlags & FLAG_SPEAKAS_INITED) { + if (mSpeakAs == SpeakAs::Other) { + return mSpeakAsCounter; + } + return this; + } + + if (mFlags & FLAG_SPEAKAS_VISITED) { + // loop detected + mFlags |= FLAG_SPEAKAS_LOOP; + return nullptr; + } + + CounterStyle* speakAsCounter; + ComputeRawSpeakAs(mSpeakAs, speakAsCounter); + + bool inLoop = false; + if (mSpeakAs != SpeakAs::Other) { + mSpeakAsCounter = nullptr; + } else if (!speakAsCounter->IsCustomStyle()) { + mSpeakAsCounter = speakAsCounter; + } else { + mFlags |= FLAG_SPEAKAS_VISITED; + CounterStyle* target = + static_cast(speakAsCounter)->ComputeSpeakAs(); + mFlags &= ~FLAG_SPEAKAS_VISITED; + + if (target) { + NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_LOOP), + "Invalid state for speak-as loop detecting"); + mSpeakAsCounter = target; + } else { + mSpeakAs = GetSpeakAsAutoValue(); + mSpeakAsCounter = nullptr; + if (mFlags & FLAG_SPEAKAS_LOOP) { + mFlags &= ~FLAG_SPEAKAS_LOOP; + } else { + inLoop = true; + } + } + } + + mFlags |= FLAG_SPEAKAS_INITED; + if (inLoop) { + return nullptr; + } + return mSpeakAsCounter ? mSpeakAsCounter : this; +} + +// This method will recursively figure out mExtends in the whole chain. +// It will return nullptr if the caller is in a loop, and return this +// otherwise. To detect the loop, this method marks the style VISITED +// before the recursive call. When a VISITED style is reached again, the +// loop is detected, and flag LOOP will be marked on the first style in +// loop. mExtends of all counter styles in loop will be set to decimal +// according to the spec. +CounterStyle* CustomCounterStyle::ComputeExtends() { + if (!IsExtendsSystem() || mExtends) { + return this; + } + if (mFlags & FLAG_EXTENDS_VISITED) { + // loop detected + mFlags |= FLAG_EXTENDS_LOOP; + return nullptr; + } + + nsAtom* extended = Servo_CounterStyleRule_GetExtended(mRule); + CounterStyle* nextCounter = mManager->ResolveCounterStyle(extended); + CounterStyle* target = nextCounter; + if (nextCounter->IsCustomStyle()) { + mFlags |= FLAG_EXTENDS_VISITED; + target = static_cast(nextCounter)->ComputeExtends(); + mFlags &= ~FLAG_EXTENDS_VISITED; + } + + if (target) { + NS_ASSERTION(!(mFlags & FLAG_EXTENDS_LOOP), + "Invalid state for extends loop detecting"); + mExtends = nextCounter; + return this; + } else { + mExtends = CounterStyleManager::GetDecimalStyle(); + if (mFlags & FLAG_EXTENDS_LOOP) { + mFlags &= ~FLAG_EXTENDS_LOOP; + return this; + } else { + return nullptr; + } + } +} + +CounterStyle* CustomCounterStyle::GetExtends() { + if (!mExtends) { + // Any extends loop will be eliminated in the method below. + ComputeExtends(); + } + return mExtends; +} + +CounterStyle* CustomCounterStyle::GetExtendsRoot() { + if (!mExtendsRoot) { + CounterStyle* extended = GetExtends(); + mExtendsRoot = extended; + if (extended->IsCustomStyle()) { + CustomCounterStyle* custom = static_cast(extended); + if (custom->IsExtendsSystem()) { + // This will make mExtendsRoot in the whole extends chain be + // set recursively, which could save work when part of a chain + // is shared by multiple counter styles. + mExtendsRoot = custom->GetExtendsRoot(); + } + } + } + return mExtendsRoot; +} + +AnonymousCounterStyle::AnonymousCounterStyle(const nsAString& aContent) + : CounterStyle(ListStyle::Custom), + mSingleString(true), + mSymbolsType(StyleSymbolsType::Cyclic) { + mSymbols.SetCapacity(1); + mSymbols.AppendElement(aContent); +} + +AnonymousCounterStyle::AnonymousCounterStyle(StyleSymbolsType aType, + nsTArray aSymbols) + : CounterStyle(ListStyle::Custom), + mSingleString(false), + mSymbolsType(aType), + mSymbols(std::move(aSymbols)) {} + +/* virtual */ +void AnonymousCounterStyle::GetPrefix(nsAString& aResult) { + aResult.Truncate(); +} + +/* virtual */ +void AnonymousCounterStyle::GetSuffix(nsAString& aResult) { + if (IsSingleString()) { + aResult.Truncate(); + } else { + aResult = ' '; + } +} + +/* virtual */ +bool AnonymousCounterStyle::IsBullet() { + // Only use ::-moz-list-bullet for cyclic system + return mSymbolsType == StyleSymbolsType::Cyclic; +} + +/* virtual */ +void AnonymousCounterStyle::GetNegative(NegativeType& aResult) { + aResult.before.AssignLiteral(u"-"); + aResult.after.Truncate(); +} + +/* virtual */ +bool AnonymousCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { + switch (mSymbolsType) { + case StyleSymbolsType::Cyclic: + case StyleSymbolsType::Numeric: + case StyleSymbolsType::Fixed: + return true; + case StyleSymbolsType::Alphabetic: + case StyleSymbolsType::Symbolic: + return aOrdinal >= 1; + default: + MOZ_ASSERT_UNREACHABLE("Invalid system."); + return false; + } +} + +/* virtual */ +bool AnonymousCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { + return AnonymousCounterStyle::IsOrdinalInRange(aOrdinal); +} + +/* virtual */ +void AnonymousCounterStyle::GetPad(PadType& aResult) { + aResult.width = 0; + aResult.symbol.Truncate(); +} + +/* virtual */ +CounterStyle* AnonymousCounterStyle::GetFallback() { + return CounterStyleManager::GetDecimalStyle(); +} + +StyleCounterSystem AnonymousCounterStyle::GetSystem() const { + switch (mSymbolsType) { + case StyleSymbolsType::Cyclic: + return StyleCounterSystem::Cyclic; + case StyleSymbolsType::Numeric: + return StyleCounterSystem::Numeric; + case StyleSymbolsType::Fixed: + return StyleCounterSystem::Fixed; + case StyleSymbolsType::Alphabetic: + return StyleCounterSystem::Alphabetic; + case StyleSymbolsType::Symbolic: + return StyleCounterSystem::Symbolic; + } + MOZ_ASSERT_UNREACHABLE("Unknown symbols() type"); + return StyleCounterSystem::Cyclic; +} + +/* virtual */ +SpeakAs AnonymousCounterStyle::GetSpeakAs() { + return GetDefaultSpeakAsForSystem(GetSystem()); +} + +/* virtual */ +bool AnonymousCounterStyle::UseNegativeSign() { + return SystemUsesNegativeSign(GetSystem()); +} + +/* virtual */ +bool AnonymousCounterStyle::GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsRTL) { + switch (mSymbolsType) { + case StyleSymbolsType::Cyclic: + return GetCyclicCounterText(aOrdinal, aResult, mSymbols); + case StyleSymbolsType::Numeric: + return GetNumericCounterText(aOrdinal, aResult, mSymbols); + case StyleSymbolsType::Fixed: + return GetFixedCounterText(aOrdinal, aResult, 1, mSymbols); + case StyleSymbolsType::Alphabetic: + return GetAlphabeticCounterText(aOrdinal, aResult, mSymbols); + case StyleSymbolsType::Symbolic: + return GetSymbolicCounterText(aOrdinal, aResult, mSymbols); + } + MOZ_ASSERT_UNREACHABLE("Invalid system."); + return false; +} + +bool CounterStyle::IsDependentStyle() const { + switch (mStyle) { + // CustomCounterStyle + case ListStyle::Custom: + // DependentBuiltinCounterStyle + case ListStyle::JapaneseInformal: + case ListStyle::JapaneseFormal: + case ListStyle::KoreanHangulFormal: + case ListStyle::KoreanHanjaInformal: + case ListStyle::KoreanHanjaFormal: + case ListStyle::SimpChineseInformal: + case ListStyle::SimpChineseFormal: + case ListStyle::TradChineseInformal: + case ListStyle::TradChineseFormal: + return true; + + // BuiltinCounterStyle + default: + return false; + } +} + +void CounterStyle::GetCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, nsAString& aResult, + bool& aIsRTL) { + bool success = IsOrdinalInRange(aOrdinal); + aIsRTL = false; + + if (success) { + // generate initial representation + bool useNegativeSign = UseNegativeSign(); + nsAutoString initialText; + CounterValue ordinal; + if (!useNegativeSign) { + ordinal = aOrdinal; + } else { + CheckedInt absolute(Abs(aOrdinal)); + ordinal = absolute.isValid() ? absolute.value() + : std::numeric_limits::max(); + } + success = GetInitialCounterText(ordinal, aWritingMode, initialText, aIsRTL); + + // add pad & negative, build the final result + if (success) { + aResult.Truncate(); + if (useNegativeSign && aOrdinal < 0) { + NegativeType negative; + GetNegative(negative); + aResult.Append(negative.before); + // There is nothing between the suffix part of negative and initial + // representation, so we append it directly here. + initialText.Append(negative.after); + } + PadType pad; + GetPad(pad); + int32_t diff = + pad.width - + narrow_cast(unicode::CountGraphemeClusters(initialText) + + unicode::CountGraphemeClusters(aResult)); + if (diff > 0) { + auto length = pad.symbol.Length(); + if (diff > LENGTH_LIMIT || length > LENGTH_LIMIT || + diff * length > LENGTH_LIMIT) { + success = false; + } else if (length > 0) { + for (int32_t i = 0; i < diff; ++i) { + aResult.Append(pad.symbol); + } + } + } + if (success) { + aResult.Append(initialText); + } + } + } + + if (!success) { + CallFallbackStyle(aOrdinal, aWritingMode, aResult, aIsRTL); + } +} + +/* virtual */ +void CounterStyle::GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsBullet) { + bool isRTL; // we don't care about direction for spoken text + aIsBullet = false; + switch (GetSpeakAs()) { + case SpeakAs::Bullets: + aResult.Assign(kDiscCharacter); + aIsBullet = true; + break; + case SpeakAs::Numbers: + DecimalToText(aOrdinal, aResult); + break; + case SpeakAs::Spellout: + // we currently do not actually support 'spell-out', + // so 'words' is used instead. + case SpeakAs::Words: + GetCounterText(aOrdinal, WritingMode(), aResult, isRTL); + break; + case SpeakAs::Other: + // This should be processed by CustomCounterStyle + MOZ_ASSERT_UNREACHABLE("Invalid speak-as value"); + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown speak-as value"); + break; + } +} + +/* virtual */ +void CounterStyle::CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) { + GetFallback()->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); +} + +CounterStyleManager::CounterStyleManager(nsPresContext* aPresContext) + : mPresContext(aPresContext) { + // Insert the static styles into cache table + mStyles.InsertOrUpdate(nsGkAtoms::none, GetNoneStyle()); + mStyles.InsertOrUpdate(nsGkAtoms::decimal, GetDecimalStyle()); + mStyles.InsertOrUpdate(nsGkAtoms::disc, GetDiscStyle()); +} + +CounterStyleManager::~CounterStyleManager() { + MOZ_ASSERT(!mPresContext, "Disconnect should have been called"); +} + +void CounterStyleManager::DestroyCounterStyle(CounterStyle* aCounterStyle) { + if (aCounterStyle->IsCustomStyle()) { + MOZ_ASSERT(!aCounterStyle->AsAnonymous(), + "Anonymous counter styles " + "are not managed by CounterStyleManager"); + static_cast(aCounterStyle)->Destroy(); + } else if (aCounterStyle->IsDependentStyle()) { + static_cast(aCounterStyle)->Destroy(); + } else { + MOZ_ASSERT_UNREACHABLE("Builtin counter styles should not be destroyed"); + } +} + +void CounterStyleManager::Disconnect() { + CleanRetiredStyles(); + for (CounterStyle* style : mStyles.Values()) { + if (style->IsDependentStyle()) { + DestroyCounterStyle(style); + } + } + mStyles.Clear(); + mPresContext = nullptr; +} + +CounterStyle* CounterStyleManager::ResolveCounterStyle(nsAtom* aName) { + MOZ_ASSERT(NS_IsMainThread()); + CounterStyle* data = GetCounterStyle(aName); + if (data) { + return data; + } + + // Names are compared case-sensitively here. Predefined names should + // have been lowercased by the parser. + ServoStyleSet* styleSet = mPresContext->StyleSet(); + auto* rule = styleSet->CounterStyleRuleForName(aName); + if (rule) { + MOZ_ASSERT(Servo_CounterStyleRule_GetName(rule) == aName); + data = new (mPresContext) CustomCounterStyle(this, rule); + } else { + for (const BuiltinCounterStyle& item : gBuiltinStyleTable) { + if (item.GetStyleName() == aName) { + const auto style = item.GetStyle(); + data = item.IsDependentStyle() + ? new (mPresContext) + DependentBuiltinCounterStyle(style, this) + : GetBuiltinStyle(style); + break; + } + } + } + if (!data) { + data = GetDecimalStyle(); + } + mStyles.InsertOrUpdate(aName, data); + return data; +} + +/* static */ +CounterStyle* CounterStyleManager::GetBuiltinStyle(ListStyle aStyle) { + MOZ_ASSERT(size_t(aStyle) < ArrayLength(gBuiltinStyleTable), + "Require a valid builtin style constant"); + MOZ_ASSERT(!gBuiltinStyleTable[size_t(aStyle)].IsDependentStyle(), + "Cannot get dependent builtin style"); + // No method of BuiltinCounterStyle mutates the struct itself, so it + // should be fine to cast const away. + return const_cast(&gBuiltinStyleTable[size_t(aStyle)]); +} + +bool CounterStyleManager::NotifyRuleChanged() { + bool changed = false; + for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) { + CounterStyle* style = iter.Data(); + bool toBeUpdated = false; + bool toBeRemoved = false; + ServoStyleSet* styleSet = mPresContext->StyleSet(); + auto* newRule = styleSet->CounterStyleRuleForName(iter.Key()); + if (!newRule) { + if (style->IsCustomStyle()) { + toBeRemoved = true; + } + } else { + if (!style->IsCustomStyle()) { + toBeRemoved = true; + } else { + auto custom = static_cast(style); + if (custom->GetRule() != newRule) { + toBeRemoved = true; + } else { + auto generation = Servo_CounterStyleRule_GetGeneration(newRule); + if (custom->GetRuleGeneration() != generation) { + toBeUpdated = true; + custom->ResetCachedData(); + } + } + } + } + changed = changed || toBeUpdated || toBeRemoved; + if (toBeRemoved) { + if (style->IsDependentStyle()) { + // Add object to retired list so we can clean them up later. + mRetiredStyles.AppendElement(style); + } + iter.Remove(); + } + } + + if (changed) { + for (CounterStyle* style : mStyles.Values()) { + if (style->IsCustomStyle()) { + CustomCounterStyle* custom = static_cast(style); + custom->ResetDependentData(); + } + // There is no dependent data cached in DependentBuiltinCounterStyle + // instances, so we don't need to reset their data. + } + } + return changed; +} + +void CounterStyleManager::CleanRetiredStyles() { + nsTArray list(std::move(mRetiredStyles)); + for (CounterStyle* style : list) { + DestroyCounterStyle(style); + } +} + +} // namespace mozilla diff --git a/layout/style/CounterStyleManager.h b/layout/style/CounterStyleManager.h new file mode 100644 index 0000000000..b6d9c2a0a9 --- /dev/null +++ b/layout/style/CounterStyleManager.h @@ -0,0 +1,355 @@ +/* -*- 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_CounterStyleManager_h_ +#define mozilla_CounterStyleManager_h_ + +#include "nsGkAtoms.h" +#include "nsStringFwd.h" +#include "nsTHashMap.h" +#include "nsHashKeys.h" + +#include "nsStyleConsts.h" + +#include "mozilla/Attributes.h" + +class nsPresContext; + +namespace mozilla { + +enum class SpeakAs : uint8_t { + Bullets = 0, + Numbers = 1, + Words = 2, + Spellout = 3, + Other = 255 +}; + +class WritingMode; + +typedef int32_t CounterValue; + +class CounterStyleManager; +class AnonymousCounterStyle; + +struct NegativeType; +struct PadType; + +class CounterStyle { + protected: + explicit constexpr CounterStyle(ListStyle aStyle) : mStyle(aStyle) {} + + private: + CounterStyle(const CounterStyle& aOther) = delete; + void operator=(const CounterStyle& other) = delete; + + public: + constexpr ListStyle GetStyle() const { return mStyle; } + bool IsNone() const { return mStyle == ListStyle::None; } + bool IsCustomStyle() const { return mStyle == ListStyle::Custom; } + // A style is dependent if it depends on the counter style manager. + // Custom styles are certainly dependent. In addition, some builtin + // styles are dependent for fallback. + bool IsDependentStyle() const; + + virtual void GetPrefix(nsAString& aResult) = 0; + virtual void GetSuffix(nsAString& aResult) = 0; + void GetCounterText(CounterValue aOrdinal, WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL); + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsBullet); + + // XXX This method could be removed once ::-moz-list-bullet and + // ::-moz-list-number are completely merged into ::marker. + virtual bool IsBullet() = 0; + + virtual void GetNegative(NegativeType& aResult) = 0; + /** + * This method returns whether an ordinal is in the range of this + * counter style. Note that, it is possible that an ordinal in range + * is rejected by the generating algorithm. + */ + virtual bool IsOrdinalInRange(CounterValue aOrdinal) = 0; + /** + * This method returns whether an ordinal is in the default range of + * this counter style. This is the effective range when no 'range' + * descriptor is specified. + */ + virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) = 0; + virtual void GetPad(PadType& aResult) = 0; + virtual CounterStyle* GetFallback() = 0; + virtual SpeakAs GetSpeakAs() = 0; + virtual bool UseNegativeSign() = 0; + + virtual void CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, nsAString& aResult, + bool& aIsRTL); + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) = 0; + + virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; } + + protected: + const ListStyle mStyle; +}; + +class AnonymousCounterStyle final : public CounterStyle { + public: + explicit AnonymousCounterStyle(const nsAString& aContent); + AnonymousCounterStyle(StyleSymbolsType, nsTArray aSymbols); + + virtual void GetPrefix(nsAString& aResult) override; + virtual void GetSuffix(nsAString& aResult) override; + virtual bool IsBullet() override; + + virtual void GetNegative(NegativeType& aResult) override; + virtual bool IsOrdinalInRange(CounterValue aOrdinal) override; + virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; + virtual void GetPad(PadType& aResult) override; + virtual CounterStyle* GetFallback() override; + virtual SpeakAs GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, bool& aIsRTL) override; + + virtual AnonymousCounterStyle* AsAnonymous() override { return this; } + + bool IsSingleString() const { return mSingleString; } + auto GetSymbols() const { return Span{mSymbols}; } + + StyleCounterSystem GetSystem() const; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle) + + private: + ~AnonymousCounterStyle() = default; + + bool mSingleString; + StyleSymbolsType mSymbolsType; + nsTArray mSymbols; +}; + +// A smart pointer to CounterStyle. It either owns a reference to an +// anonymous counter style, or weakly refers to a named counter style +// managed by counter style manager. +class CounterStylePtr { + public: + CounterStylePtr() : mRaw(0) {} + CounterStylePtr(const CounterStylePtr& aOther) : mRaw(aOther.mRaw) { + if (!mRaw) { + return; + } + switch (GetType()) { + case eAnonymousCounterStyle: + AsAnonymous()->AddRef(); + break; + case eAtom: + AsAtom()->AddRef(); + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown type"); + break; + } + } + CounterStylePtr(CounterStylePtr&& aOther) : mRaw(aOther.mRaw) { + aOther.mRaw = 0; + } + ~CounterStylePtr() { Reset(); } + + CounterStylePtr& operator=(const CounterStylePtr& aOther) { + if (this != &aOther) { + Reset(); + new (this) CounterStylePtr(aOther); + } + return *this; + } + CounterStylePtr& operator=(CounterStylePtr&& aOther) { + if (this != &aOther) { + Reset(); + mRaw = aOther.mRaw; + aOther.mRaw = 0; + } + return *this; + } + CounterStylePtr& operator=(decltype(nullptr)) { + Reset(); + return *this; + } + CounterStylePtr& operator=(nsStaticAtom* aStaticAtom) { + Reset(); + mRaw = reinterpret_cast(aStaticAtom) | eAtom; + return *this; + } + CounterStylePtr& operator=(already_AddRefed aAtom) { + Reset(); + mRaw = reinterpret_cast(aAtom.take()) | eAtom; + return *this; + } + CounterStylePtr& operator=(AnonymousCounterStyle* aCounterStyle) { + Reset(); + if (aCounterStyle) { + CounterStyle* raw = do_AddRef(aCounterStyle).take(); + mRaw = reinterpret_cast(raw) | eAnonymousCounterStyle; + } + return *this; + } + + // TODO(emilio): Make CounterStyle have a single representation, either by + // removing CounterStylePtr or by moving this representation to Rust. + static CounterStylePtr FromStyle(const StyleCounterStyle& aStyle) { + CounterStylePtr ret; + if (aStyle.IsName()) { + ret = do_AddRef(aStyle.AsName().AsAtom()); + } else { + StyleSymbolsType type = aStyle.AsSymbols()._0; + Span symbols = aStyle.AsSymbols()._1._0.AsSpan(); + nsTArray transcoded(symbols.Length()); + for (const auto& symbol : symbols) { + MOZ_ASSERT(symbol.IsString(), "Should not have in symbols()"); + transcoded.AppendElement( + NS_ConvertUTF8toUTF16(symbol.AsString().AsString())); + } + ret = new AnonymousCounterStyle(type, std::move(transcoded)); + } + return ret; + } + + explicit operator bool() const { return !!mRaw; } + bool operator!() const { return !mRaw; } + bool operator==(const CounterStylePtr& aOther) const { + // FIXME(emilio): For atoms this is all right, but for symbols doesn't this + // cause us to compare as unequal all the time, even if the specified + // symbols didn't change? + return mRaw == aOther.mRaw; + } + bool operator!=(const CounterStylePtr& aOther) const { + return mRaw != aOther.mRaw; + } + + nsAtom* AsAtom() const { + MOZ_ASSERT(IsAtom()); + return reinterpret_cast(mRaw & ~eMask); + } + AnonymousCounterStyle* AsAnonymous() const { + MOZ_ASSERT(IsAnonymous()); + return static_cast( + reinterpret_cast(mRaw & ~eMask)); + } + + bool IsAtom() const { return GetType() == eAtom; } + bool IsAnonymous() const { return GetType() == eAnonymousCounterStyle; } + + bool IsNone() const { return IsAtom() && AsAtom() == nsGkAtoms::none; } + + private: + enum Type : uintptr_t { + eAnonymousCounterStyle = 0, + eAtom = 1, + eMask = 1, + }; + + static_assert(alignof(CounterStyle) >= 1 << eMask, + "We're gonna tag the pointer, so it better fit"); + static_assert(alignof(nsAtom) >= 1 << eMask, + "We're gonna tag the pointer, so it better fit"); + + Type GetType() const { return static_cast(mRaw & eMask); } + + void Reset() { + if (!mRaw) { + return; + } + switch (GetType()) { + case eAnonymousCounterStyle: + AsAnonymous()->Release(); + break; + case eAtom: + AsAtom()->Release(); + break; + default: + MOZ_ASSERT_UNREACHABLE("Unknown type"); + break; + } + mRaw = 0; + } + + // mRaw contains the pointer, and its last bit is used to store the type of + // the pointer. + // If the type is eAtom, the pointer owns a reference to an nsAtom + // (potentially null). + // If the type is eAnonymousCounterStyle, it owns a reference to an + // anonymous counter style (never null). + uintptr_t mRaw; +}; + +class CounterStyleManager final { + private: + ~CounterStyleManager(); + + public: + explicit CounterStyleManager(nsPresContext* aPresContext); + + void Disconnect(); + + bool IsInitial() const { + // only 'none', 'decimal', and 'disc' + return mStyles.Count() == 3; + } + + // Returns the counter style object for the given name from the style + // table if it is already built, and nullptr otherwise. + CounterStyle* GetCounterStyle(nsAtom* aName) const { + return mStyles.Get(aName); + } + // Same as GetCounterStyle but try to build the counter style object + // rather than returning nullptr if that hasn't been built. + CounterStyle* ResolveCounterStyle(nsAtom* aName); + CounterStyle* ResolveCounterStyle(const CounterStylePtr& aPtr) { + if (aPtr.IsAtom()) { + return ResolveCounterStyle(aPtr.AsAtom()); + } + return aPtr.AsAnonymous(); + } + + static CounterStyle* GetBuiltinStyle(ListStyle aStyle); + static CounterStyle* GetNoneStyle() { + return GetBuiltinStyle(ListStyle::None); + } + static CounterStyle* GetDecimalStyle() { + return GetBuiltinStyle(ListStyle::Decimal); + } + static CounterStyle* GetDiscStyle() { + return GetBuiltinStyle(ListStyle::Disc); + } + + // This method will scan all existing counter styles generated by this + // manager, and remove or mark data dirty accordingly. It returns true + // if any counter style is changed, false elsewise. This method should + // be called when any counter style may be affected. + bool NotifyRuleChanged(); + // NotifyRuleChanged will evict no longer needed counter styles into + // mRetiredStyles, and this function destroys all objects listed there. + // It should be called only after no one may ever use those objects. + void CleanRetiredStyles(); + + nsPresContext* PresContext() const { return mPresContext; } + + NS_INLINE_DECL_REFCOUNTING(CounterStyleManager) + + private: + void DestroyCounterStyle(CounterStyle* aCounterStyle); + + nsPresContext* mPresContext; + nsTHashMap, CounterStyle*> mStyles; + nsTArray mRetiredStyles; +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_CounterStyleManager_h_) */ diff --git a/layout/style/DeclarationBlock.cpp b/layout/style/DeclarationBlock.cpp new file mode 100644 index 0000000000..03d07049b9 --- /dev/null +++ b/layout/style/DeclarationBlock.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 "mozilla/DeclarationBlock.h" + +#include "mozilla/css/Rule.h" + +#include "nsCSSProps.h" +#include "nsIMemoryReporter.h" + +namespace mozilla { + +MOZ_DEFINE_MALLOC_SIZE_OF(ServoDeclarationBlockMallocSizeOf) +MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoDeclarationBlockEnclosingSizeOf) + +size_t DeclarationBlock::SizeofIncludingThis(MallocSizeOf aMallocSizeOf) { + size_t n = aMallocSizeOf(this); + n += Servo_DeclarationBlock_SizeOfIncludingThis( + ServoDeclarationBlockMallocSizeOf, ServoDeclarationBlockEnclosingSizeOf, + mRaw.get()); + return n; +} + +bool DeclarationBlock::OwnerIsReadOnly() const { + css::Rule* rule = GetOwningRule(); + return rule && rule->IsReadOnly(); +} + +} // namespace mozilla diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h new file mode 100644 index 0000000000..837ab74bce --- /dev/null +++ b/layout/style/DeclarationBlock.h @@ -0,0 +1,245 @@ +/* -*- 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/. */ + +/* + * representation of a declaration block in a CSS stylesheet, or of + * a style attribute + */ + +#ifndef mozilla_DeclarationBlock_h +#define mozilla_DeclarationBlock_h + +#include "mozilla/Atomics.h" +#include "mozilla/ServoBindings.h" + +#include "nsCSSPropertyID.h" +#include "nsString.h" + +namespace mozilla { + +class AttributeStyles; +namespace css { +class Declaration; +class Rule; +} // namespace css + +class DeclarationBlock final { + DeclarationBlock(const DeclarationBlock& aCopy) + : mRaw(Servo_DeclarationBlock_Clone(aCopy.mRaw).Consume()), + mImmutable(false), + mIsDirty(false) { + mContainer.mRaw = 0; + } + + public: + explicit DeclarationBlock(already_AddRefed aRaw) + : mRaw(aRaw), mImmutable(false), mIsDirty(false) { + mContainer.mRaw = 0; + } + + DeclarationBlock() + : DeclarationBlock(Servo_DeclarationBlock_CreateEmpty().Consume()) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DeclarationBlock) + + already_AddRefed Clone() const { + return do_AddRef(new DeclarationBlock(*this)); + } + + /** + * Return whether |this| may be modified. + */ + bool IsMutable() const { return !mImmutable; } + + /** + * Crash in debug builds if |this| cannot be modified. + */ + void AssertMutable() const { + MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable"); + MOZ_ASSERT(!OwnerIsReadOnly(), "User Agent sheets shouldn't be modified"); + } + + /** + * Mark this declaration as unmodifiable. + */ + void SetImmutable() { mImmutable = true; } + + /** + * Return whether |this| has been restyled after modified. + */ + bool IsDirty() const { return mIsDirty; } + + /** + * Mark this declaration as dirty. + */ + void SetDirty() { mIsDirty = true; } + + /** + * Mark this declaration as not dirty. + */ + void UnsetDirty() { mIsDirty = false; } + + /** + * Copy |this|, if necessary to ensure that it can be modified. + */ + already_AddRefed EnsureMutable() { + MOZ_ASSERT(!OwnerIsReadOnly()); + + if (!IsDirty()) { + // In stylo, the old DeclarationBlock is stored in element's rule node + // tree directly, to avoid new values replacing the DeclarationBlock in + // the tree directly, we need to copy the old one here if we haven't yet + // copied. As a result the new value does not replace rule node tree until + // traversal happens. + // + // FIXME(emilio, bug 1606413): This is a hack for ::first-line and + // transitions starting due to CSSOM changes when other transitions are + // already running. Try to simplify this setup, so that rule tree updates + // find the mutated declaration block properly rather than having to + // insert the cloned declaration in the tree. + return Clone(); + } + + if (!IsMutable()) { + return Clone(); + } + + return do_AddRef(this); + } + + void SetOwningRule(css::Rule* aRule) { + MOZ_ASSERT(!mContainer.mOwningRule || !aRule, + "should never overwrite one rule with another"); + mContainer.mOwningRule = aRule; + } + + css::Rule* GetOwningRule() const { + if (mContainer.mRaw & 0x1) { + return nullptr; + } + return mContainer.mOwningRule; + } + + void SetAttributeStyles(AttributeStyles* aAttributeStyles) { + MOZ_ASSERT(!mContainer.mAttributeStyles || !aAttributeStyles, + "should never overwrite one sheet with another"); + mContainer.mAttributeStyles = aAttributeStyles; + if (aAttributeStyles) { + mContainer.mRaw |= uintptr_t(1); + } + } + + AttributeStyles* GetAttributeStyles() const { + if (!(mContainer.mRaw & 0x1)) { + return nullptr; + } + auto c = mContainer; + c.mRaw &= ~uintptr_t(1); + return c.mAttributeStyles; + } + + bool IsReadOnly() const; + + size_t SizeofIncludingThis(MallocSizeOf); + + static already_AddRefed FromCssText( + const nsACString& aCssText, URLExtraData* aExtraData, + nsCompatibility aMode, css::Loader* aLoader, StyleCssRuleType aRuleType) { + RefPtr raw = + Servo_ParseStyleAttribute(&aCssText, aExtraData, aMode, aLoader, + aRuleType) + .Consume(); + return MakeAndAddRef(raw.forget()); + } + + static already_AddRefed FromCssText( + const nsAString& aCssText, URLExtraData* aExtraData, + nsCompatibility aMode, css::Loader* aLoader, StyleCssRuleType aRuleType) { + NS_ConvertUTF16toUTF8 value(aCssText); + return FromCssText(value, aExtraData, aMode, aLoader, aRuleType); + } + + StyleLockedDeclarationBlock* Raw() const { return mRaw; } + + void ToString(nsACString& aResult) const { + Servo_DeclarationBlock_GetCssText(mRaw, &aResult); + } + + uint32_t Count() const { return Servo_DeclarationBlock_Count(mRaw); } + + bool GetNthProperty(uint32_t aIndex, nsACString& aReturn) const { + aReturn.Truncate(); + return Servo_DeclarationBlock_GetNthProperty(mRaw, aIndex, &aReturn); + } + + void GetPropertyValue(const nsACString& aProperty, nsACString& aValue) const { + Servo_DeclarationBlock_GetPropertyValue(mRaw, &aProperty, &aValue); + } + + void GetPropertyValueByID(nsCSSPropertyID aPropID, nsACString& aValue) const { + Servo_DeclarationBlock_GetPropertyValueById(mRaw, aPropID, &aValue); + } + + bool GetPropertyIsImportant(const nsACString& aProperty) const { + return Servo_DeclarationBlock_GetPropertyIsImportant(mRaw, &aProperty); + } + + // Returns whether the property was removed. + bool RemoveProperty(const nsACString& aProperty, + DeclarationBlockMutationClosure aClosure = {}) { + AssertMutable(); + return Servo_DeclarationBlock_RemoveProperty(mRaw, &aProperty, aClosure); + } + + // Returns whether the property was removed. + bool RemovePropertyByID(nsCSSPropertyID aProperty, + DeclarationBlockMutationClosure aClosure = {}) { + AssertMutable(); + return Servo_DeclarationBlock_RemovePropertyById(mRaw, aProperty, aClosure); + } + + private: + ~DeclarationBlock() = default; + + bool OwnerIsReadOnly() const; + + union { + // We only ever have one of these since we have a AttributeStyles only for + // style attributes, and style attributes never have an owning rule. It's a + // AttributeStyles if the low bit is set. + + uintptr_t mRaw; + + // The style rule that owns this declaration. May be null. + css::Rule* mOwningRule; + + // The AttributeStyles that is responsible for this declaration. Only + // non-null for style attributes. + AttributeStyles* mAttributeStyles; + } mContainer; + + RefPtr mRaw; + + // set when declaration put in the rule tree; + bool mImmutable; + + // True if this declaration has not been restyled after modified. + // + // Since we can clear this flag from style worker threads, we use an Atomic. + // + // Note that although a single DeclarationBlock can be shared between + // different rule nodes (due to the style="" attribute cache), whenever a + // DeclarationBlock has its mIsDirty flag set to true, we always clone it to + // a unique object first. So when we clear this flag during Servo traversal, + // we know that we are clearing it on a DeclarationBlock that has a single + // reference, and there is no problem with another user of the same + // DeclarationBlock thinking that it is not dirty. + Atomic mIsDirty; +}; + +} // namespace mozilla + +#endif // mozilla_DeclarationBlock_h diff --git a/layout/style/DocumentMatchingFunction.h b/layout/style/DocumentMatchingFunction.h new file mode 100644 index 0000000000..f22161c339 --- /dev/null +++ b/layout/style/DocumentMatchingFunction.h @@ -0,0 +1,30 @@ +/* -*- 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_css_DocumentMatchingFunction_h +#define mozilla_css_DocumentMatchingFunction_h + +namespace mozilla { +namespace css { + +/** + * Enum defining the type of matching function for a @-moz-document rule + * condition. + */ +enum class DocumentMatchingFunction { + URL = 0, + URLPrefix, + Domain, + RegExp, + MediaDocument, + PlainTextDocument, + UnobservableDocument, +}; + +} // namespace css +} // namespace mozilla + +#endif // mozilla_css_DocumentMatchingFunction_h diff --git a/layout/style/DocumentStyleRootIterator.cpp b/layout/style/DocumentStyleRootIterator.cpp new file mode 100644 index 0000000000..0d9595c434 --- /dev/null +++ b/layout/style/DocumentStyleRootIterator.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/. */ + +#include "DocumentStyleRootIterator.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/Element.h" +#include "nsContentUtils.h" + +namespace mozilla { + +DocumentStyleRootIterator::DocumentStyleRootIterator(nsINode* aStyleRoot) + : mPosition(0) { + MOZ_COUNT_CTOR(DocumentStyleRootIterator); + MOZ_ASSERT(aStyleRoot); + if (aStyleRoot->IsElement()) { + mStyleRoots.AppendElement(aStyleRoot->AsElement()); + return; + } + + dom::Document* doc = aStyleRoot->OwnerDoc(); + MOZ_ASSERT(doc == aStyleRoot); + if (dom::Element* root = doc->GetRootElement()) { + mStyleRoots.AppendElement(root); + } + nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(doc, mStyleRoots); +} + +dom::Element* DocumentStyleRootIterator::GetNextStyleRoot() { + for (;;) { + if (mPosition >= mStyleRoots.Length()) { + return nullptr; + } + + nsIContent* next = mStyleRoots[mPosition]; + ++mPosition; + + if (next->IsElement()) { + return next->AsElement(); + } + } +} + +} // namespace mozilla diff --git a/layout/style/DocumentStyleRootIterator.h b/layout/style/DocumentStyleRootIterator.h new file mode 100644 index 0000000000..84f48d02d7 --- /dev/null +++ b/layout/style/DocumentStyleRootIterator.h @@ -0,0 +1,45 @@ +/* -*- 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 DocumentStyleRootIterator_h +#define DocumentStyleRootIterator_h + +#include "nsTArray.h" + +class nsIContent; + +class nsINode; + +namespace mozilla { + +namespace dom { +class Element; +} // namespace dom + +/** + * DocumentStyleRootIterator traverses the roots of the document from the + * perspective of the Servo-backed style system. In the general case, this + * will first traverse the document root, followed by any document level + * native anonymous content. + * + * If the caller passes an element to the constructor rather than the document, + * that element (and nothing else) is returned from GetNextStyleRoot. + */ +class DocumentStyleRootIterator { + public: + explicit DocumentStyleRootIterator(nsINode* aStyleRoot); + MOZ_COUNTED_DTOR(DocumentStyleRootIterator) + + dom::Element* GetNextStyleRoot(); + + private: + AutoTArray mStyleRoots; + uint32_t mPosition; +}; + +} // namespace mozilla + +#endif // DocumentStyleRootIterator_h diff --git a/layout/style/ErrorReporter.cpp b/layout/style/ErrorReporter.cpp new file mode 100644 index 0000000000..6335c695ac --- /dev/null +++ b/layout/style/ErrorReporter.cpp @@ -0,0 +1,277 @@ +/* -*- 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/. */ + +/* diagnostic reporting for CSS style sheet parser */ + +#include "mozilla/css/ErrorReporter.h" + +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Loader.h" +#include "mozilla/Preferences.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/Components.h" +#include "nsIConsoleService.h" +#include "mozilla/dom/Document.h" +#include "nsComponentManagerUtils.h" +#include "nsIDocShell.h" +#include "nsIFactory.h" +#include "nsINode.h" +#include "nsIScriptError.h" +#include "nsIStringBundle.h" +#include "nsServiceManagerUtils.h" +#include "nsStyleUtil.h" +#include "nsThreadUtils.h" +#include "nsNetUtil.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +namespace { +class ShortTermURISpecCache : public Runnable { + public: + ShortTermURISpecCache() + : Runnable("ShortTermURISpecCache"), mPending(false) {} + + nsString const& GetSpec(nsIURI* aURI) { + if (mURI != aURI) { + mURI = aURI; + + if (NS_FAILED(NS_GetSanitizedURIStringFromURI(mURI, mSpec))) { + mSpec.AssignLiteral("[nsIURI::GetSpec failed]"); + } + } + return mSpec; + } + + bool IsInUse() const { return mURI != nullptr; } + bool IsPending() const { return mPending; } + void SetPending() { mPending = true; } + + // When invoked as a runnable, zap the cache. + NS_IMETHOD Run() override { + mURI = nullptr; + mSpec.Truncate(); + mPending = false; + return NS_OK; + } + + private: + nsCOMPtr mURI; + nsString mSpec; + bool mPending; +}; + +} // namespace + +bool ErrorReporter::sInitialized = false; + +static nsIConsoleService* sConsoleService; +static nsIFactory* sScriptErrorFactory; +static nsIStringBundle* sStringBundle; +static ShortTermURISpecCache* sSpecCache; + +void ErrorReporter::InitGlobals() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!sInitialized, "should not have been called"); + + sInitialized = true; + + nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (!cs) { + return; + } + + nsCOMPtr sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID); + if (!sf) { + return; + } + + nsCOMPtr sbs = components::StringBundle::Service(); + if (!sbs) { + return; + } + + nsCOMPtr sb; + nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties", + getter_AddRefs(sb)); + if (NS_FAILED(rv) || !sb) { + return; + } + + cs.forget(&sConsoleService); + sf.forget(&sScriptErrorFactory); + sb.forget(&sStringBundle); +} + +namespace mozilla { +namespace css { + +/* static */ +void ErrorReporter::ReleaseGlobals() { + NS_IF_RELEASE(sConsoleService); + NS_IF_RELEASE(sScriptErrorFactory); + NS_IF_RELEASE(sStringBundle); + NS_IF_RELEASE(sSpecCache); +} + +uint64_t ErrorReporter::FindInnerWindowId(const StyleSheet* aSheet, + const Loader* aLoader) { + if (aSheet) { + if (uint64_t id = aSheet->FindOwningWindowInnerID()) { + return id; + } + } + if (aLoader) { + if (Document* doc = aLoader->GetDocument()) { + return doc->InnerWindowID(); + } + } + return 0; +} + +ErrorReporter::ErrorReporter(uint64_t aInnerWindowId) + : mInnerWindowId(aInnerWindowId) { + EnsureGlobalsInitialized(); +} + +ErrorReporter::~ErrorReporter() { + MOZ_ASSERT(NS_IsMainThread()); + // Schedule deferred cleanup for cached data. We want to strike a + // balance between performance and memory usage, so we only allow + // short-term caching. + if (sSpecCache && sSpecCache->IsInUse() && !sSpecCache->IsPending()) { + nsCOMPtr runnable(sSpecCache); + nsresult rv = SchedulerGroup::Dispatch(runnable.forget()); + if (NS_FAILED(rv)) { + // Peform the "deferred" cleanup immediately if the dispatch fails. + sSpecCache->Run(); + } else { + sSpecCache->SetPending(); + } + } +} + +bool ErrorReporter::ShouldReportErrors(const Document& aDoc) { + MOZ_ASSERT(NS_IsMainThread()); + nsIDocShell* shell = aDoc.GetDocShell(); + if (!shell) { + return false; + } + + bool report = false; + shell->GetCssErrorReportingEnabled(&report); + return report; +} + +static nsINode* SheetOwner(const StyleSheet& aSheet) { + if (nsINode* owner = aSheet.GetOwnerNode()) { + return owner; + } + + auto* associated = aSheet.GetAssociatedDocumentOrShadowRoot(); + return associated ? &associated->AsNode() : nullptr; +} + +bool ErrorReporter::ShouldReportErrors(const StyleSheet* aSheet, + const Loader* aLoader) { + MOZ_ASSERT(NS_IsMainThread()); + + if (!StaticPrefs::layout_css_report_errors()) { + return false; + } + + if (aSheet) { + nsINode* owner = SheetOwner(*aSheet); + if (owner && ShouldReportErrors(*owner->OwnerDoc())) { + return true; + } + } + + if (aLoader && aLoader->GetDocument() && + ShouldReportErrors(*aLoader->GetDocument())) { + return true; + } + + return false; +} + +void ErrorReporter::OutputError(const nsACString& aSourceLine, + const nsACString& aSelectors, + uint32_t aLineNumber, uint32_t aColNumber, + nsIURI* aURI) { + nsAutoString errorLine; + // This could be a really long string for minified CSS; just leave it empty + // if we OOM. + if (!AppendUTF8toUTF16(aSourceLine, errorLine, fallible)) { + errorLine.Truncate(); + } + + nsAutoString selectors; + if (!AppendUTF8toUTF16(aSelectors, selectors, fallible)) { + selectors.Truncate(); + } + + if (mError.IsEmpty()) { + return; + } + + nsAutoString fileName; + if (aURI) { + if (!sSpecCache) { + sSpecCache = new ShortTermURISpecCache; + NS_ADDREF(sSpecCache); + } + fileName = sSpecCache->GetSpec(aURI); + } else { + fileName.AssignLiteral("from DOM"); + } + + nsresult rv; + nsCOMPtr errorObject = + do_CreateInstance(sScriptErrorFactory, &rv); + + if (NS_SUCCEEDED(rv)) { + // It is safe to used InitWithSanitizedSource because fileName is + // an already anonymized uri spec. + rv = errorObject->InitWithSanitizedSource( + mError, fileName, errorLine, aLineNumber, aColNumber, + nsIScriptError::warningFlag, "CSS Parser", mInnerWindowId); + + if (NS_SUCCEEDED(rv)) { + errorObject->SetCssSelectors(selectors); + sConsoleService->LogMessage(errorObject); + } + } + + mError.Truncate(); +} + +void ErrorReporter::AddToError(const nsString& aErrorText) { + if (mError.IsEmpty()) { + mError = aErrorText; + } else { + mError.AppendLiteral(" "); + mError.Append(aErrorText); + } +} + +void ErrorReporter::ReportUnexpected(const char* aMessage) { + nsAutoString str; + sStringBundle->GetStringFromName(aMessage, str); + AddToError(str); +} + +void ErrorReporter::ReportUnexpectedUnescaped( + const char* aMessage, const nsTArray& aParam) { + nsAutoString str; + sStringBundle->FormatStringFromName(aMessage, aParam, str); + AddToError(str); +} + +} // namespace css +} // namespace mozilla diff --git a/layout/style/ErrorReporter.h b/layout/style/ErrorReporter.h new file mode 100644 index 0000000000..3f24cf089f --- /dev/null +++ b/layout/style/ErrorReporter.h @@ -0,0 +1,73 @@ +/* -*- 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/. */ + +/* diagnostic reporting for CSS style sheet parser */ + +#ifndef mozilla_css_ErrorReporter_h_ +#define mozilla_css_ErrorReporter_h_ + +#include "nsString.h" + +struct nsCSSToken; +class nsIURI; + +namespace mozilla { +class StyleSheet; + +namespace dom { +class Document; +} + +namespace css { + +class Loader; + +// FIXME(emilio): Probably better to call this ErrorBuilder or something? +class MOZ_STACK_CLASS ErrorReporter final { + public: + explicit ErrorReporter(uint64_t aInnerWindowId); + ~ErrorReporter(); + + static void ReleaseGlobals(); + static void EnsureGlobalsInitialized() { + if (MOZ_UNLIKELY(!sInitialized)) { + InitGlobals(); + } + } + + static bool ShouldReportErrors(const dom::Document&); + static bool ShouldReportErrors(const StyleSheet*, const Loader*); + static uint64_t FindInnerWindowId(const StyleSheet*, const Loader*); + + void OutputError(const nsACString& aSource, const nsACString& aSelectors, + uint32_t aLineNumber, uint32_t aColNumber, nsIURI* aURI); + void ClearError(); + + // In all overloads of ReportUnexpected, aMessage is a stringbundle + // name, which will be processed as a format string with the + // indicated number of parameters. + + // no parameters + void ReportUnexpected(const char* aMessage); + // one parameter which has already been escaped appropriately + void ReportUnexpectedUnescaped(const char* aMessage, + const nsTArray& aParam); + + private: + void AddToError(const nsString& aErrorText); + static void InitGlobals(); + + static bool sInitialized; + static bool sReportErrors; + + nsString mError; + const uint64_t mInnerWindowId; +}; + +} // namespace css +} // namespace mozilla + +#endif // mozilla_css_ErrorReporter_h_ diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp new file mode 100644 index 0000000000..f1a90334ea --- /dev/null +++ b/layout/style/FontFace.cpp @@ -0,0 +1,314 @@ +/* -*- 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 "mozilla/dom/FontFace.h" + +#include +#include "gfxFontUtils.h" +#include "mozilla/dom/CSSFontFaceRule.h" +#include "mozilla/dom/FontFaceBinding.h" +#include "mozilla/dom/FontFaceImpl.h" +#include "mozilla/dom/FontFaceSet.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/UnionTypes.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSParser.h" +#include "mozilla/ServoStyleSet.h" +#include "mozilla/ServoUtils.h" +#include "mozilla/StaticPrefs_layout.h" +#include "nsStyleUtil.h" + +namespace mozilla { +namespace dom { + +// -- Utility functions ------------------------------------------------------ + +template +static void GetDataFrom(const T& aObject, uint8_t*& aBuffer, + uint32_t& aLength) { + MOZ_ASSERT(!aBuffer); + // We need to use malloc here because the gfxUserFontEntry will be calling + // free on it, so the Vector's default AllocPolicy (MallocAllocPolicy) is + // fine. + Maybe> buffer = + aObject.template CreateFromData>(); + if (buffer.isNothing()) { + return; + } + aLength = buffer->length(); + aBuffer = buffer->extractOrCopyRawBuffer(); +} + +// -- FontFace --------------------------------------------------------------- + +NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded) + tmp->Destroy(); + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFace) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FontFace) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FontFace) + +FontFace::FontFace(nsIGlobalObject* aParent) + : mParent(aParent), mLoadedRejection(NS_OK) {} + +FontFace::~FontFace() { + // Assert that we don't drop any FontFace objects during a Servo traversal, + // since PostTraversalTask objects can hold raw pointers to FontFaces. + MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); + Destroy(); +} + +void FontFace::Destroy() { mImpl->Destroy(); } + +JSObject* FontFace::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return FontFace_Binding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed FontFace::CreateForRule( + nsIGlobalObject* aGlobal, FontFaceSet* aFontFaceSet, + StyleLockedFontFaceRule* aRule) { + FontFaceSetImpl* setImpl = aFontFaceSet->GetImpl(); + MOZ_ASSERT(setImpl); + + RefPtr obj = new FontFace(aGlobal); + obj->mImpl = FontFaceImpl::CreateForRule(obj, setImpl, aRule); + return obj.forget(); +} + +already_AddRefed FontFace::Constructor( + const GlobalObject& aGlobal, const nsACString& aFamily, + const UTF8StringOrArrayBufferOrArrayBufferView& aSource, + const FontFaceDescriptors& aDescriptors, ErrorResult& aRv) { + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + + FontFaceSet* set = global->GetFonts(); + if (NS_WARN_IF(!set)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + FontFaceSetImpl* setImpl = set->GetImpl(); + if (NS_WARN_IF(!setImpl)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr obj = new FontFace(global); + obj->mImpl = new FontFaceImpl(obj, setImpl); + if (!obj->mImpl->SetDescriptors(aFamily, aDescriptors)) { + return obj.forget(); + } + + if (aSource.IsUTF8String()) { + obj->mImpl->InitializeSourceURL(aSource.GetAsUTF8String()); + } else { + uint8_t* buffer = nullptr; + uint32_t length = 0; + if (aSource.IsArrayBuffer()) { + GetDataFrom(aSource.GetAsArrayBuffer(), buffer, length); + } else if (aSource.IsArrayBufferView()) { + GetDataFrom(aSource.GetAsArrayBufferView(), buffer, length); + } else { + MOZ_ASSERT_UNREACHABLE("Unhandled source type!"); + return nullptr; + } + + obj->mImpl->InitializeSourceBuffer(buffer, length); + } + + return obj.forget(); +} + +void FontFace::GetFamily(nsACString& aResult) { mImpl->GetFamily(aResult); } + +void FontFace::SetFamily(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetFamily(aValue, aRv); +} + +void FontFace::GetStyle(nsACString& aResult) { mImpl->GetStyle(aResult); } + +void FontFace::SetStyle(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetStyle(aValue, aRv); +} + +void FontFace::GetWeight(nsACString& aResult) { mImpl->GetWeight(aResult); } + +void FontFace::SetWeight(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetWeight(aValue, aRv); +} + +void FontFace::GetStretch(nsACString& aResult) { mImpl->GetStretch(aResult); } + +void FontFace::SetStretch(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetStretch(aValue, aRv); +} + +void FontFace::GetUnicodeRange(nsACString& aResult) { + mImpl->GetUnicodeRange(aResult); +} + +void FontFace::SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetUnicodeRange(aValue, aRv); +} + +void FontFace::GetVariant(nsACString& aResult) { mImpl->GetVariant(aResult); } + +void FontFace::SetVariant(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetVariant(aValue, aRv); +} + +void FontFace::GetFeatureSettings(nsACString& aResult) { + mImpl->GetFeatureSettings(aResult); +} + +void FontFace::SetFeatureSettings(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetFeatureSettings(aValue, aRv); +} + +void FontFace::GetVariationSettings(nsACString& aResult) { + mImpl->GetVariationSettings(aResult); +} + +void FontFace::SetVariationSettings(const nsACString& aValue, + ErrorResult& aRv) { + mImpl->SetVariationSettings(aValue, aRv); +} + +void FontFace::GetDisplay(nsACString& aResult) { mImpl->GetDisplay(aResult); } + +void FontFace::SetDisplay(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetDisplay(aValue, aRv); +} + +void FontFace::GetAscentOverride(nsACString& aResult) { + mImpl->GetAscentOverride(aResult); +} + +void FontFace::SetAscentOverride(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetAscentOverride(aValue, aRv); +} + +void FontFace::GetDescentOverride(nsACString& aResult) { + mImpl->GetDescentOverride(aResult); +} + +void FontFace::SetDescentOverride(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetDescentOverride(aValue, aRv); +} + +void FontFace::GetLineGapOverride(nsACString& aResult) { + mImpl->GetLineGapOverride(aResult); +} + +void FontFace::SetLineGapOverride(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetLineGapOverride(aValue, aRv); +} + +void FontFace::GetSizeAdjust(nsACString& aResult) { + mImpl->GetSizeAdjust(aResult); +} + +void FontFace::SetSizeAdjust(const nsACString& aValue, ErrorResult& aRv) { + mImpl->SetSizeAdjust(aValue, aRv); +} + +FontFaceLoadStatus FontFace::Status() { return mImpl->Status(); } + +Promise* FontFace::Load(ErrorResult& aRv) { + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + mImpl->Load(aRv); + return mLoaded; +} + +Promise* FontFace::GetLoaded(ErrorResult& aRv) { + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return mLoaded; +} + +void FontFace::MaybeResolve() { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + + if (!mLoaded) { + return; + } + + if (ServoStyleSet* ss = gfxFontUtils::CurrentServoStyleSet()) { + // See comments in Gecko_GetFontMetrics. + ss->AppendTask(PostTraversalTask::ResolveFontFaceLoadedPromise(this)); + return; + } + + mLoaded->MaybeResolve(this); +} + +void FontFace::MaybeReject(nsresult aResult) { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + + if (ServoStyleSet* ss = gfxFontUtils::CurrentServoStyleSet()) { + // See comments in Gecko_GetFontMetrics. + ss->AppendTask( + PostTraversalTask::RejectFontFaceLoadedPromise(this, aResult)); + return; + } + + if (mLoaded) { + mLoaded->MaybeReject(aResult); + } else if (mLoadedRejection == NS_OK) { + mLoadedRejection = aResult; + } +} + +void FontFace::EnsurePromise() { + if (mLoaded || !mImpl || !mParent) { + return; + } + + ErrorResult rv; + mLoaded = Promise::Create(mParent, rv); + + if (mImpl->Status() == FontFaceLoadStatus::Loaded) { + mLoaded->MaybeResolve(this); + } else if (mLoadedRejection != NS_OK) { + mLoaded->MaybeReject(mLoadedRejection); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/FontFace.h b/layout/style/FontFace.h new file mode 100644 index 0000000000..bdb33a0449 --- /dev/null +++ b/layout/style/FontFace.h @@ -0,0 +1,126 @@ +/* -*- 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_dom_FontFace_h +#define mozilla_dom_FontFace_h + +#include "mozilla/dom/FontFaceBinding.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/Maybe.h" +#include "mozilla/ServoStyleConsts.h" +#include "gfxUserFontSet.h" +#include "nsCSSPropertyID.h" +#include "nsCSSValue.h" +#include "nsWrapperCache.h" + +class gfxFontFaceBufferSource; +class nsIGlobalObject; + +namespace mozilla { +struct CSSFontFaceDescriptors; +class PostTraversalTask; +struct StyleLockedFontFaceRule; + +namespace dom { +class CSSFontFaceRule; +class FontFaceBufferSource; +struct FontFaceDescriptors; +class FontFaceImpl; +class FontFaceSet; +class FontFaceSetImpl; +class Promise; +class UTF8StringOrArrayBufferOrArrayBufferView; +} // namespace dom +} // namespace mozilla + +namespace mozilla::dom { + +class FontFace final : public nsISupports, public nsWrapperCache { + friend class mozilla::PostTraversalTask; + + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FontFace) + + nsIGlobalObject* GetParentObject() const { return mParent; } + JSObject* WrapObject(JSContext*, JS::Handle aGivenProto) override; + + static already_AddRefed CreateForRule( + nsIGlobalObject* aGlobal, FontFaceSet* aFontFaceSet, + StyleLockedFontFaceRule* aRule); + + // Web IDL + static already_AddRefed Constructor( + const GlobalObject& aGlobal, const nsACString& aFamily, + const UTF8StringOrArrayBufferOrArrayBufferView& aSource, + const FontFaceDescriptors& aDescriptors, ErrorResult& aRV); + + void GetFamily(nsACString& aResult); + void SetFamily(const nsACString& aValue, ErrorResult& aRv); + void GetStyle(nsACString& aResult); + void SetStyle(const nsACString& aValue, ErrorResult& aRv); + void GetWeight(nsACString& aResult); + void SetWeight(const nsACString& aValue, ErrorResult& aRv); + void GetStretch(nsACString& aResult); + void SetStretch(const nsACString& aValue, ErrorResult& aRv); + void GetUnicodeRange(nsACString& aResult); + void SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv); + void GetVariant(nsACString& aResult); + void SetVariant(const nsACString& aValue, ErrorResult& aRv); + void GetFeatureSettings(nsACString& aResult); + void SetFeatureSettings(const nsACString& aValue, ErrorResult& aRv); + void GetVariationSettings(nsACString& aResult); + void SetVariationSettings(const nsACString& aValue, ErrorResult& aRv); + void GetDisplay(nsACString& aResult); + void SetDisplay(const nsACString& aValue, ErrorResult& aRv); + void GetAscentOverride(nsACString& aResult); + void SetAscentOverride(const nsACString& aValue, ErrorResult& aRv); + void GetDescentOverride(nsACString& aResult); + void SetDescentOverride(const nsACString& aValue, ErrorResult& aRv); + void GetLineGapOverride(nsACString& aResult); + void SetLineGapOverride(const nsACString& aValue, ErrorResult& aRv); + void GetSizeAdjust(nsACString& aResult); + void SetSizeAdjust(const nsACString& aValue, ErrorResult& aRv); + + FontFaceLoadStatus Status(); + Promise* Load(ErrorResult& aRv); + Promise* GetLoaded(ErrorResult& aRv); + + FontFaceImpl* GetImpl() const { return mImpl; } + + void Destroy(); + void MaybeResolve(); + void MaybeReject(nsresult aResult); + + private: + explicit FontFace(nsIGlobalObject* aParent); + ~FontFace(); + + /** + * Returns and takes ownership of the buffer storing the font data. + */ + void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength); + + // Creates mLoaded if it doesn't already exist. It may immediately resolve or + // reject mLoaded based on mStatus and mLoadedRejection. + void EnsurePromise(); + + nsCOMPtr mParent; + + RefPtr mImpl; + + // A Promise that is fulfilled once the font represented by this FontFace is + // loaded, and is rejected if the load fails. This promise is created lazily + // when JS asks for it. + RefPtr mLoaded; + + // Saves the rejection code for mLoaded if mLoaded hasn't been created yet. + nsresult mLoadedRejection; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFace_h) diff --git a/layout/style/FontFaceImpl.cpp b/layout/style/FontFaceImpl.cpp new file mode 100644 index 0000000000..9d1f7c8c5b --- /dev/null +++ b/layout/style/FontFaceImpl.cpp @@ -0,0 +1,840 @@ +/* -*- 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 "mozilla/dom/FontFaceImpl.h" + +#include +#include "gfxFontUtils.h" +#include "gfxPlatformFontList.h" +#include "mozilla/dom/CSSFontFaceRule.h" +#include "mozilla/dom/FontFaceBinding.h" +#include "mozilla/dom/FontFaceSetImpl.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/dom/UnionTypes.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSParser.h" +#include "mozilla/ServoUtils.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/dom/Document.h" +#include "nsStyleUtil.h" + +namespace mozilla { +namespace dom { + +// -- FontFaceBufferSource --------------------------------------------------- + +/** + * An object that wraps a FontFace object and exposes its ArrayBuffer + * or ArrayBufferView data in a form the user font set can consume. + */ +class FontFaceBufferSource : public gfxFontFaceBufferSource { + public: + FontFaceBufferSource(uint8_t* aBuffer, uint32_t aLength) + : mBuffer(aBuffer), mLength(aLength) {} + + void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) override { + MOZ_ASSERT(mBuffer, + "only call TakeBuffer once on a given " + "FontFaceBufferSource object"); + aBuffer = mBuffer; + aLength = mLength; + mBuffer = nullptr; + mLength = 0; + } + + private: + ~FontFaceBufferSource() override { + if (mBuffer) { + free(mBuffer); + } + } + + uint8_t* mBuffer; + uint32_t mLength; +}; + +// -- FontFaceImpl ----------------------------------------------------------- + +FontFaceImpl::FontFaceImpl(FontFace* aOwner, FontFaceSetImpl* aFontFaceSet) + : mOwner(aOwner), + mStatus(FontFaceLoadStatus::Unloaded), + mSourceType(SourceType(0)), + mFontFaceSet(aFontFaceSet), + mUnicodeRangeDirty(true), + mInFontFaceSet(false) {} + +FontFaceImpl::~FontFaceImpl() { + // Assert that we don't drop any FontFace objects during a Servo traversal, + // since PostTraversalTask objects can hold raw pointers to FontFaces. + MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); + + SetUserFontEntry(nullptr); +} + +#ifdef DEBUG +void FontFaceImpl::AssertIsOnOwningThread() const { + mFontFaceSet->AssertIsOnOwningThread(); +} +#endif + +void FontFaceImpl::Destroy() { + mInFontFaceSet = false; + SetUserFontEntry(nullptr); + mOwner = nullptr; +} + +static FontFaceLoadStatus LoadStateToStatus( + gfxUserFontEntry::UserFontLoadState aLoadState) { + switch (aLoadState) { + case gfxUserFontEntry::UserFontLoadState::STATUS_NOT_LOADED: + return FontFaceLoadStatus::Unloaded; + case gfxUserFontEntry::UserFontLoadState::STATUS_LOAD_PENDING: + case gfxUserFontEntry::UserFontLoadState::STATUS_LOADING: + return FontFaceLoadStatus::Loading; + case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED: + return FontFaceLoadStatus::Loaded; + case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED: + return FontFaceLoadStatus::Error; + } + MOZ_ASSERT_UNREACHABLE("invalid aLoadState value"); + return FontFaceLoadStatus::Error; +} + +already_AddRefed FontFaceImpl::CreateForRule( + FontFace* aOwner, FontFaceSetImpl* aFontFaceSet, + StyleLockedFontFaceRule* aRule) { + RefPtr obj = new FontFaceImpl(aOwner, aFontFaceSet); + obj->mRule = aRule; + obj->mSourceType = eSourceType_FontFaceRule; + obj->mInFontFaceSet = true; + return obj.forget(); +} + +void FontFaceImpl::InitializeSourceURL(const nsACString& aURL) { + MOZ_ASSERT(mOwner); + mSourceType = eSourceType_URLs; + + IgnoredErrorResult rv; + SetDescriptor(eCSSFontDesc_Src, aURL, rv); + if (rv.Failed()) { + mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); + SetStatus(FontFaceLoadStatus::Error); + } +} + +void FontFaceImpl::InitializeSourceBuffer(uint8_t* aBuffer, uint32_t aLength) { + MOZ_ASSERT(mOwner); + MOZ_ASSERT(!mBufferSource); + mSourceType = FontFaceImpl::eSourceType_Buffer; + + if (aBuffer) { + mBufferSource = new FontFaceBufferSource(aBuffer, aLength); + } + + SetStatus(FontFaceLoadStatus::Loading); + DoLoad(); +} + +void FontFaceImpl::GetFamily(nsACString& aResult) { + GetDesc(eCSSFontDesc_Family, aResult); +} + +void FontFaceImpl::SetFamily(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Family, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetStyle(nsACString& aResult) { + GetDesc(eCSSFontDesc_Style, aResult); +} + +void FontFaceImpl::SetStyle(const nsACString& aValue, ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_Style, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetWeight(nsACString& aResult) { + GetDesc(eCSSFontDesc_Weight, aResult); +} + +void FontFaceImpl::SetWeight(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Weight, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetStretch(nsACString& aResult) { + GetDesc(eCSSFontDesc_Stretch, aResult); +} + +void FontFaceImpl::SetStretch(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetUnicodeRange(nsACString& aResult) { + GetDesc(eCSSFontDesc_UnicodeRange, aResult); +} + +void FontFaceImpl::SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetVariant(nsACString& aResult) { + // XXX Just expose the font-variant descriptor as "normal" until we + // support it properly (bug 1055385). + aResult.AssignLiteral("normal"); +} + +void FontFaceImpl::SetVariant(const nsACString& aValue, ErrorResult& aRv) { + // XXX Ignore assignments to variant until we support font-variant + // descriptors (bug 1055385). +} + +void FontFaceImpl::GetFeatureSettings(nsACString& aResult) { + GetDesc(eCSSFontDesc_FontFeatureSettings, aResult); +} + +void FontFaceImpl::SetFeatureSettings(const nsACString& aValue, + ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetVariationSettings(nsACString& aResult) { + GetDesc(eCSSFontDesc_FontVariationSettings, aResult); +} + +void FontFaceImpl::SetVariationSettings(const nsACString& aValue, + ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + if (SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetDisplay(nsACString& aResult) { + GetDesc(eCSSFontDesc_Display, aResult); +} + +void FontFaceImpl::SetDisplay(const nsACString& aValue, ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_Display, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetAscentOverride(nsACString& aResult) { + GetDesc(eCSSFontDesc_AscentOverride, aResult); +} + +void FontFaceImpl::SetAscentOverride(const nsACString& aValue, + ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_AscentOverride, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetDescentOverride(nsACString& aResult) { + GetDesc(eCSSFontDesc_DescentOverride, aResult); +} + +void FontFaceImpl::SetDescentOverride(const nsACString& aValue, + ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_DescentOverride, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetLineGapOverride(nsACString& aResult) { + GetDesc(eCSSFontDesc_LineGapOverride, aResult); +} + +void FontFaceImpl::SetLineGapOverride(const nsACString& aValue, + ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_LineGapOverride, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::GetSizeAdjust(nsACString& aResult) { + GetDesc(eCSSFontDesc_SizeAdjust, aResult); +} + +void FontFaceImpl::SetSizeAdjust(const nsACString& aValue, ErrorResult& aRv) { + if (SetDescriptor(eCSSFontDesc_SizeAdjust, aValue, aRv)) { + DescriptorUpdated(); + } +} + +void FontFaceImpl::DescriptorUpdated() { + // If we haven't yet initialized mUserFontEntry, no need to do anything here; + // we'll respect the updated descriptor when the time comes to create it. + if (!mUserFontEntry) { + return; + } + + gfxUserFontAttributes attr; + RefPtr newEntry; + if (GetAttributes(attr)) { + newEntry = mFontFaceSet->FindOrCreateUserFontEntryFromFontFace( + this, std::move(attr), StyleOrigin::Author); + } + SetUserFontEntry(newEntry); + + // Behind the scenes, this will actually update the existing entry and return + // it, rather than create a new one. + + if (mInFontFaceSet) { + mFontFaceSet->MarkUserFontSetDirty(); + } + for (auto& set : mOtherFontFaceSets) { + set->MarkUserFontSetDirty(); + } +} + +FontFaceLoadStatus FontFaceImpl::Status() { return mStatus; } + +void FontFaceImpl::Load(ErrorResult& aRv) { + mFontFaceSet->FlushUserFontSet(); + + // Calling Load on a FontFace constructed with an ArrayBuffer data source, + // or on one that is already loading (or has finished loading), has no + // effect. + if (mSourceType == eSourceType_Buffer || + mStatus != FontFaceLoadStatus::Unloaded) { + return; + } + + // Calling the user font entry's Load method will end up setting our + // status to Loading, but the spec requires us to set it to Loading + // here. + SetStatus(FontFaceLoadStatus::Loading); + + DoLoad(); +} + +gfxUserFontEntry* FontFaceImpl::CreateUserFontEntry() { + if (!mUserFontEntry) { + MOZ_ASSERT(!HasRule(), + "Rule backed FontFace objects should already have a user font " + "entry by the time Load() can be called on them"); + + gfxUserFontAttributes attr; + if (GetAttributes(attr)) { + RefPtr newEntry = + mFontFaceSet->FindOrCreateUserFontEntryFromFontFace( + this, std::move(attr), StyleOrigin::Author); + if (newEntry) { + SetUserFontEntry(newEntry); + } + } + } + + return mUserFontEntry; +} + +void FontFaceImpl::DoLoad() { + if (!CreateUserFontEntry()) { + return; + } + mUserFontEntry->Load(); +} + +void FontFaceImpl::SetStatus(FontFaceLoadStatus aStatus) { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + + if (mStatus == aStatus) { + return; + } + + if (aStatus < mStatus) { + // We're being asked to go backwards in status! Normally, this shouldn't + // happen. But it can if the FontFace had a user font entry that had + // loaded, but then was given a new one by FontFaceSet::InsertRuleFontFace + // if we used a local() rule. For now, just ignore the request to + // go backwards in status. + return; + } + + mStatus = aStatus; + + if (mInFontFaceSet) { + mFontFaceSet->OnFontFaceStatusChanged(this); + } + + for (FontFaceSetImpl* otherSet : mOtherFontFaceSets) { + otherSet->OnFontFaceStatusChanged(this); + } + + UpdateOwnerPromise(); +} + +void FontFaceImpl::UpdateOwnerPromise() { + if (!mFontFaceSet->IsOnOwningThread()) { + mFontFaceSet->DispatchToOwningThread( + "FontFaceImpl::UpdateOwnerPromise", + [self = RefPtr{this}] { self->UpdateOwnerPromise(); }); + return; + } + + if (NS_WARN_IF(!mOwner)) { + return; + } + + if (mStatus == FontFaceLoadStatus::Loaded) { + mOwner->MaybeResolve(); + } else if (mStatus == FontFaceLoadStatus::Error) { + if (mSourceType == eSourceType_Buffer) { + mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); + } else { + mOwner->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); + } + } +} + +// Boolean result indicates whether the value of the descriptor was actually +// changed. +bool FontFaceImpl::SetDescriptor(nsCSSFontDesc aFontDesc, + const nsACString& aValue, ErrorResult& aRv) { + // FIXME We probably don't need to distinguish between this anymore + // since we have common backend now. + NS_ASSERTION(!HasRule(), "we don't handle rule backed FontFace objects yet"); + if (HasRule()) { + return false; + } + + RefPtr url = mFontFaceSet->GetURLExtraData(); + if (NS_WARN_IF(!url)) { + // This should only happen on worker threads, where we failed to initialize + // the worker before it was shutdown. + aRv.ThrowInvalidStateError("Missing URLExtraData"); + return false; + } + + // FIXME(heycam): Should not allow modification of FontFaces that are + // CSS-connected and whose rule is read only. + bool changed; + if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &aValue, url, + &changed)) { + aRv.ThrowSyntaxError("Invalid font descriptor"); + return false; + } + + if (!changed) { + return false; + } + + if (aFontDesc == eCSSFontDesc_UnicodeRange) { + mUnicodeRangeDirty = true; + } + + return true; +} + +bool FontFaceImpl::SetDescriptors(const nsACString& aFamily, + const FontFaceDescriptors& aDescriptors) { + MOZ_ASSERT(!HasRule()); + MOZ_ASSERT(!mDescriptors); + + mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume(); + + // Helper to call SetDescriptor and return true on success, false on failure. + auto setDesc = [=](nsCSSFontDesc aDesc, const nsACString& aVal) -> bool { + IgnoredErrorResult rv; + SetDescriptor(aDesc, aVal, rv); + return !rv.Failed(); + }; + + // Parse all of the mDescriptors in aInitializer, which are the values + // we got from the JS constructor. + if (!setDesc(eCSSFontDesc_Family, aFamily) || + !setDesc(eCSSFontDesc_Style, aDescriptors.mStyle) || + !setDesc(eCSSFontDesc_Weight, aDescriptors.mWeight) || + !setDesc(eCSSFontDesc_Stretch, aDescriptors.mStretch) || + !setDesc(eCSSFontDesc_UnicodeRange, aDescriptors.mUnicodeRange) || + !setDesc(eCSSFontDesc_FontFeatureSettings, + aDescriptors.mFeatureSettings) || + (StaticPrefs::layout_css_font_variations_enabled() && + !setDesc(eCSSFontDesc_FontVariationSettings, + aDescriptors.mVariationSettings)) || + !setDesc(eCSSFontDesc_Display, aDescriptors.mDisplay) || + ((!setDesc(eCSSFontDesc_AscentOverride, aDescriptors.mAscentOverride) || + !setDesc(eCSSFontDesc_DescentOverride, aDescriptors.mDescentOverride) || + !setDesc(eCSSFontDesc_LineGapOverride, + aDescriptors.mLineGapOverride))) || + (StaticPrefs::layout_css_size_adjust_enabled() && + !setDesc(eCSSFontDesc_SizeAdjust, aDescriptors.mSizeAdjust))) { + // XXX Handle font-variant once we support it (bug 1055385). + + // If any of the descriptors failed to parse, none of them should be set + // on the FontFace. + mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume(); + + if (mOwner) { + mOwner->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); + } + + SetStatus(FontFaceLoadStatus::Error); + return false; + } + + return true; +} + +void FontFaceImpl::GetDesc(nsCSSFontDesc aDescID, nsACString& aResult) const { + aResult.Truncate(); + Servo_FontFaceRule_GetDescriptorCssText(GetData(), aDescID, &aResult); + + // Fill in a default value for missing descriptors. + if (aResult.IsEmpty()) { + if (aDescID == eCSSFontDesc_UnicodeRange) { + aResult.AssignLiteral("U+0-10FFFF"); + } else if (aDescID == eCSSFontDesc_Display) { + aResult.AssignLiteral("auto"); + } else if (aDescID != eCSSFontDesc_Family && aDescID != eCSSFontDesc_Src) { + aResult.AssignLiteral("normal"); + } + } +} + +void FontFaceImpl::SetUserFontEntry(gfxUserFontEntry* aEntry) { + AssertIsOnOwningThread(); + + if (mUserFontEntry == aEntry) { + return; + } + + if (mUserFontEntry) { + mUserFontEntry->RemoveFontFace(this); + } + + auto* entry = static_cast(aEntry); + if (entry) { + entry->AddFontFace(this); + } + + mUserFontEntry = entry; + + if (!mUserFontEntry) { + return; + } + + MOZ_ASSERT(mUserFontEntry->HasUserFontSet(mFontFaceSet), + "user font entry must be associated with the same user font set " + "as the FontFace"); + + // Our newly assigned user font entry might be in the process of or + // finished loading, so set our status accordingly. But only do so + // if we're not going "backwards" in status, which could otherwise + // happen in this case: + // + // new FontFace("ABC", "url(x)").load(); + // + // where the SetUserFontEntry call (from the after-initialization + // DoLoad call) comes after the author's call to load(), which set mStatus + // to Loading. + FontFaceLoadStatus newStatus = LoadStateToStatus(mUserFontEntry->LoadState()); + if (newStatus > mStatus) { + SetStatus(newStatus); + } +} + +bool FontFaceImpl::GetAttributes(gfxUserFontAttributes& aAttr) { + StyleLockedFontFaceRule* data = GetData(); + if (!data) { + return false; + } + + nsAtom* fontFamily = Servo_FontFaceRule_GetFamilyName(data); + if (!fontFamily) { + return false; + } + + aAttr.mFamilyName = nsAtomCString(fontFamily); + + StyleComputedFontWeightRange weightRange; + if (Servo_FontFaceRule_GetFontWeight(data, &weightRange)) { + aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoWeight; + aAttr.mWeight = WeightRange(FontWeight::FromFloat(weightRange._0), + FontWeight::FromFloat(weightRange._1)); + } + + StyleComputedFontStretchRange stretchRange; + if (Servo_FontFaceRule_GetFontStretch(data, &stretchRange)) { + aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoStretch; + aAttr.mStretch = StretchRange(stretchRange._0, stretchRange._1); + } + + auto styleDesc = StyleComputedFontStyleDescriptor::Normal(); + if (Servo_FontFaceRule_GetFontStyle(data, &styleDesc)) { + aAttr.mRangeFlags &= ~gfxFontEntry::RangeFlags::eAutoSlantStyle; + switch (styleDesc.tag) { + case StyleComputedFontStyleDescriptor::Tag::Normal: + aAttr.mStyle = SlantStyleRange(FontSlantStyle::NORMAL); + break; + case StyleComputedFontStyleDescriptor::Tag::Italic: + aAttr.mStyle = SlantStyleRange(FontSlantStyle::ITALIC); + break; + case StyleComputedFontStyleDescriptor::Tag::Oblique: + aAttr.mStyle = SlantStyleRange( + FontSlantStyle::FromFloat(styleDesc.AsOblique()._0), + FontSlantStyle::FromFloat(styleDesc.AsOblique()._1)); + break; + default: + MOZ_ASSERT_UNREACHABLE("Unhandled tag"); + } + } + + StylePercentage ascent{0}; + if (Servo_FontFaceRule_GetAscentOverride(data, &ascent)) { + aAttr.mAscentOverride = ascent._0; + } + + StylePercentage descent{0}; + if (Servo_FontFaceRule_GetDescentOverride(data, &descent)) { + aAttr.mDescentOverride = descent._0; + } + + StylePercentage lineGap{0}; + if (Servo_FontFaceRule_GetLineGapOverride(data, &lineGap)) { + aAttr.mLineGapOverride = lineGap._0; + } + + StylePercentage sizeAdjust; + if (Servo_FontFaceRule_GetSizeAdjust(data, &sizeAdjust)) { + aAttr.mSizeAdjust = sizeAdjust._0; + } + + StyleFontLanguageOverride langOverride; + if (Servo_FontFaceRule_GetFontLanguageOverride(data, &langOverride)) { + aAttr.mLanguageOverride = langOverride._0; + } + + Servo_FontFaceRule_GetFontDisplay(data, &aAttr.mFontDisplay); + Servo_FontFaceRule_GetFeatureSettings(data, &aAttr.mFeatureSettings); + Servo_FontFaceRule_GetVariationSettings(data, &aAttr.mVariationSettings); + Servo_FontFaceRule_GetSources(data, &aAttr.mSources); + aAttr.mUnicodeRanges = GetUnicodeRangeAsCharacterMap(); + return true; +} + +bool FontFaceImpl::HasLocalSrc() const { + AutoTArray components; + Servo_FontFaceRule_GetSources(GetData(), &components); + for (auto& component : components) { + if (component.tag == StyleFontFaceSourceListComponent::Tag::Local) { + return true; + } + } + return false; +} + +nsAtom* FontFaceImpl::GetFamilyName() const { + return Servo_FontFaceRule_GetFamilyName(GetData()); +} + +void FontFaceImpl::DisconnectFromRule() { + MOZ_ASSERT(HasRule()); + + // Make a copy of the descriptors. + mDescriptors = Servo_FontFaceRule_Clone(mRule).Consume(); + mRule = nullptr; + mInFontFaceSet = false; +} + +bool FontFaceImpl::HasFontData() const { + return mSourceType == eSourceType_Buffer && mBufferSource; +} + +already_AddRefed FontFaceImpl::TakeBufferSource() { + MOZ_ASSERT(mBufferSource); + return mBufferSource.forget(); +} + +bool FontFaceImpl::IsInFontFaceSet(FontFaceSetImpl* aFontFaceSet) const { + if (mFontFaceSet == aFontFaceSet) { + return mInFontFaceSet; + } + return mOtherFontFaceSets.Contains(aFontFaceSet); +} + +void FontFaceImpl::AddFontFaceSet(FontFaceSetImpl* aFontFaceSet) { + MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = true; + } else { + mOtherFontFaceSets.AppendElement(aFontFaceSet); + } +} + +void FontFaceImpl::RemoveFontFaceSet(FontFaceSetImpl* aFontFaceSet) { + MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = false; + } else { + mOtherFontFaceSets.RemoveElement(aFontFaceSet); + } + + // The caller should be holding a strong reference to the FontFaceSetImpl. + if (mUserFontEntry) { + mUserFontEntry->CheckUserFontSet(); + } +} + +gfxCharacterMap* FontFaceImpl::GetUnicodeRangeAsCharacterMap() { + if (!mUnicodeRangeDirty) { + return mUnicodeRange; + } + + size_t len; + const StyleUnicodeRange* rangesPtr = + Servo_FontFaceRule_GetUnicodeRanges(GetData(), &len); + + Span ranges(rangesPtr, len); + if (!ranges.IsEmpty()) { + RefPtr charMap = new gfxCharacterMap(); + for (auto& range : ranges) { + charMap->SetRange(range.start, range.end); + } + charMap->Compact(); + // As it's common for multiple font resources to have the same + // unicode-range list, look for an existing copy of this map to share, + // or add this one to the sharing cache if not already present. + mUnicodeRange = + gfxPlatformFontList::PlatformFontList()->FindCharMap(charMap); + } else { + mUnicodeRange = nullptr; + } + + mUnicodeRangeDirty = false; + return mUnicodeRange; +} + +// -- FontFaceImpl::Entry +// -------------------------------------------------------- + +/* virtual */ +void FontFaceImpl::Entry::SetLoadState(UserFontLoadState aLoadState) { + gfxUserFontEntry::SetLoadState(aLoadState); + FontFaceLoadStatus status = LoadStateToStatus(aLoadState); + + nsTArray> fontFaces; + { + MutexAutoLock lock(mMutex); + fontFaces.SetCapacity(mFontFaces.Length()); + for (FontFaceImpl* f : mFontFaces) { + fontFaces.AppendElement(f); + } + } + + for (FontFaceImpl* impl : fontFaces) { + auto* setImpl = impl->GetPrimaryFontFaceSet(); + if (setImpl->IsOnOwningThread()) { + impl->SetStatus(status); + } else { + setImpl->DispatchToOwningThread( + "FontFaceImpl::Entry::SetLoadState", + [self = RefPtr{impl}, status] { self->SetStatus(status); }); + } + } +} + +/* virtual */ +void FontFaceImpl::Entry::GetUserFontSets( + nsTArray>& aResult) { + MutexAutoLock lock(mMutex); + + aResult.Clear(); + + if (mFontSet) { + aResult.AppendElement(mFontSet); + } + + for (FontFaceImpl* f : mFontFaces) { + if (f->mInFontFaceSet) { + aResult.AppendElement(f->mFontFaceSet); + } + for (FontFaceSetImpl* s : f->mOtherFontFaceSets) { + aResult.AppendElement(s); + } + } + + // Remove duplicates. + aResult.Sort(); + auto it = std::unique(aResult.begin(), aResult.end()); + aResult.TruncateLength(it - aResult.begin()); +} + +/* virtual */ already_AddRefed +FontFaceImpl::Entry::GetUserFontSet() const { + MutexAutoLock lock(mMutex); + if (mFontSet) { + return do_AddRef(mFontSet); + } + if (NS_IsMainThread() && mLoadingFontSet) { + return do_AddRef(mLoadingFontSet); + } + return nullptr; +} + +void FontFaceImpl::Entry::CheckUserFontSetLocked() { + // If this is the last font containing a strong reference to the set, we need + // to clear the reference as there is no longer anything guaranteeing the set + // will be kept alive. + if (mFontSet) { + auto* set = static_cast(mFontSet); + for (FontFaceImpl* f : mFontFaces) { + if (f->mFontFaceSet == set || f->mOtherFontFaceSets.Contains(set)) { + return; + } + } + } + + // If possible, promote the most recently added FontFace and its owning + // FontFaceSetImpl as the primary set. + if (!mFontFaces.IsEmpty()) { + mFontSet = mFontFaces.LastElement()->mFontFaceSet; + } else { + mFontSet = nullptr; + } +} + +void FontFaceImpl::Entry::FindFontFaceOwners(nsTHashSet& aOwners) { + MutexAutoLock lock(mMutex); + for (FontFaceImpl* f : mFontFaces) { + if (FontFace* owner = f->GetOwner()) { + aOwners.Insert(owner); + } + } +} + +void FontFaceImpl::Entry::AddFontFace(FontFaceImpl* aFontFace) { + MutexAutoLock lock(mMutex); + mFontFaces.AppendElement(aFontFace); + CheckUserFontSetLocked(); +} + +void FontFaceImpl::Entry::RemoveFontFace(FontFaceImpl* aFontFace) { + MutexAutoLock lock(mMutex); + mFontFaces.RemoveElement(aFontFace); + CheckUserFontSetLocked(); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/FontFaceImpl.h b/layout/style/FontFaceImpl.h new file mode 100644 index 0000000000..70c06609e9 --- /dev/null +++ b/layout/style/FontFaceImpl.h @@ -0,0 +1,308 @@ +/* -*- 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_dom_FontFaceImpl_h +#define mozilla_dom_FontFaceImpl_h + +#include "mozilla/dom/FontFaceBinding.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/Maybe.h" +#include "mozilla/Mutex.h" +#include "mozilla/ServoStyleConsts.h" +#include "gfxUserFontSet.h" +#include "nsCSSPropertyID.h" +#include "nsCSSValue.h" +#include "nsTHashSet.h" + +class gfxFontFaceBufferSource; + +namespace mozilla { +struct CSSFontFaceDescriptors; +class PostTraversalTask; +struct StyleLockedFontFaceRule; +namespace dom { +class CSSFontFaceRule; +class FontFace; +class FontFaceBufferSource; +struct FontFaceDescriptors; +class FontFaceSetImpl; +class UTF8StringOrArrayBufferOrArrayBufferView; +} // namespace dom +} // namespace mozilla + +namespace mozilla::dom { + +class FontFaceImpl final { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FontFaceImpl) + + friend class mozilla::PostTraversalTask; + friend class FontFaceBufferSource; + friend class Entry; + + public: + class Entry final : public gfxUserFontEntry { + friend class FontFaceImpl; + + public: + Entry(gfxUserFontSet* aFontSet, nsTArray&& aFontFaceSrcList, + gfxUserFontAttributes&& aAttr) + : gfxUserFontEntry(std::move(aFontFaceSrcList), std::move(aAttr)), + mMutex("FontFaceImpl::Entry::mMutex"), + mFontSet(aFontSet) {} + + void SetLoadState(UserFontLoadState aLoadState) override; + void GetUserFontSets(nsTArray>& aResult) override; + already_AddRefed GetUserFontSet() const override; + + void CheckUserFontSet() { + MutexAutoLock lock(mMutex); + CheckUserFontSetLocked(); + } + +#ifdef DEBUG + bool HasUserFontSet(gfxUserFontSet* aFontSet) const { + MutexAutoLock lock(mMutex); + return mFontSet == aFontSet; + } +#endif + + void AddFontFace(FontFaceImpl* aOwner); + void RemoveFontFace(FontFaceImpl* aOwner); + void FindFontFaceOwners(nsTHashSet& aOwners); + + protected: + void CheckUserFontSetLocked() MOZ_REQUIRES(mMutex); + + mutable Mutex mMutex; + + // Font set which owns this entry; + gfxUserFontSet* MOZ_NON_OWNING_REF mFontSet MOZ_GUARDED_BY(mMutex); + + // The FontFace objects that use this user font entry. We need to store + // an array of these, not just a single pointer, since the user font + // cache can return the same entry for different FontFaces that have + // the same descriptor values and come from the same origin. + AutoTArray mFontFaces MOZ_GUARDED_BY(mMutex); + }; + +#ifdef DEBUG + void AssertIsOnOwningThread() const; +#else + void AssertIsOnOwningThread() const {} +#endif + + FontFace* GetOwner() const { + AssertIsOnOwningThread(); + return mOwner; + } + + static already_AddRefed CreateForRule( + FontFace* aOwner, FontFaceSetImpl* aFontFaceSet, + StyleLockedFontFaceRule* aRule); + + StyleLockedFontFaceRule* GetRule() { return mRule; } + + bool HasLocalSrc() const; + + bool GetAttributes(gfxUserFontAttributes& aAttr); + gfxUserFontEntry* CreateUserFontEntry(); + gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; } + void SetUserFontEntry(gfxUserFontEntry* aEntry); + + /** + * Returns whether this object is in the specified FontFaceSet. + */ + bool IsInFontFaceSet(FontFaceSetImpl* aFontFaceSet) const; + + void AddFontFaceSet(FontFaceSetImpl* aFontFaceSet); + void RemoveFontFaceSet(FontFaceSetImpl* aFontFaceSet); + + FontFaceSetImpl* GetPrimaryFontFaceSet() const { return mFontFaceSet; } + + /** + * Gets the family name of the FontFace as a raw string (such as 'Times', as + * opposed to GetFamily, which returns a CSS-escaped string, such as + * '"Times"'). Returns null if a valid family name was not available. + */ + nsAtom* GetFamilyName() const; + + /** + * Returns whether this object is CSS-connected, i.e. reflecting an + * @font-face rule. + */ + bool HasRule() const { return mRule; } + + /** + * Breaks the connection between this FontFace and its @font-face rule. + */ + void DisconnectFromRule(); + + /** + * Returns whether there is an ArrayBuffer or ArrayBufferView of font + * data. + */ + bool HasFontData() const; + + /** + * Takes the gfxFontFaceBufferSource to represent the font data + * in this object. + */ + already_AddRefed TakeBufferSource(); + + /** + * Gets a pointer to and the length of the font data stored in the + * ArrayBuffer or ArrayBufferView. + */ + bool GetData(uint8_t*& aBuffer, uint32_t& aLength); + + /** + * Returns the value of the unicode-range descriptor as a gfxCharacterMap. + */ + gfxCharacterMap* GetUnicodeRangeAsCharacterMap(); + + // Web IDL + void GetFamily(nsACString& aResult); + void SetFamily(const nsACString& aValue, ErrorResult& aRv); + void GetStyle(nsACString& aResult); + void SetStyle(const nsACString& aValue, ErrorResult& aRv); + void GetWeight(nsACString& aResult); + void SetWeight(const nsACString& aValue, ErrorResult& aRv); + void GetStretch(nsACString& aResult); + void SetStretch(const nsACString& aValue, ErrorResult& aRv); + void GetUnicodeRange(nsACString& aResult); + void SetUnicodeRange(const nsACString& aValue, ErrorResult& aRv); + void GetVariant(nsACString& aResult); + void SetVariant(const nsACString& aValue, ErrorResult& aRv); + void GetFeatureSettings(nsACString& aResult); + void SetFeatureSettings(const nsACString& aValue, ErrorResult& aRv); + void GetVariationSettings(nsACString& aResult); + void SetVariationSettings(const nsACString& aValue, ErrorResult& aRv); + void GetDisplay(nsACString& aResult); + void SetDisplay(const nsACString& aValue, ErrorResult& aRv); + void GetAscentOverride(nsACString& aResult); + void SetAscentOverride(const nsACString& aValue, ErrorResult& aRv); + void GetDescentOverride(nsACString& aResult); + void SetDescentOverride(const nsACString& aValue, ErrorResult& aRv); + void GetLineGapOverride(nsACString& aResult); + void SetLineGapOverride(const nsACString& aValue, ErrorResult& aRv); + void GetSizeAdjust(nsACString& aResult); + void SetSizeAdjust(const nsACString& aValue, ErrorResult& aRv); + + FontFaceLoadStatus Status(); + void Load(ErrorResult& aRv); + + void Destroy(); + + FontFaceImpl(FontFace* aOwner, FontFaceSetImpl* aFontFaceSet); + + void InitializeSourceURL(const nsACString& aURL); + void InitializeSourceBuffer(uint8_t* aBuffer, uint32_t aLength); + + /** + * Sets all of the descriptor values in mDescriptors using values passed + * to the JS constructor. + * Returns true on success, false if parsing any descriptor failed. + */ + bool SetDescriptors(const nsACString& aFamily, + const FontFaceDescriptors& aDescriptors); + + private: + ~FontFaceImpl(); + + // Helper function for Load. + void DoLoad(); + void UpdateOwnerPromise(); + + // Helper function for the descriptor setter methods. + // Returns true if the descriptor was modified, false if descriptor is + // unchanged (which may not be an error: check aRv for actual failure). + bool SetDescriptor(nsCSSFontDesc aFontDesc, const nsACString& aValue, + ErrorResult& aRv); + + /** + * Called when a descriptor has been modified, so font-face sets can + * be told to refresh. + */ + void DescriptorUpdated(); + + /** + * Sets the current loading status. + */ + void SetStatus(FontFaceLoadStatus aStatus); + + void GetDesc(nsCSSFontDesc aDescID, nsACString& aResult) const; + + StyleLockedFontFaceRule* GetData() const { + AssertIsOnOwningThread(); + return HasRule() ? mRule : mDescriptors; + } + + /** + * Returns and takes ownership of the buffer storing the font data. + */ + void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength); + + FontFace* MOZ_NON_OWNING_REF mOwner; + + // The @font-face rule this FontFace object is reflecting, if it is a + // rule backed FontFace. + RefPtr mRule; + + // The FontFace object's user font entry. This is initially null, but is set + // during FontFaceSet::UpdateRules and when a FontFace is explicitly loaded. + RefPtr mUserFontEntry; + + // The current load status of the font represented by this FontFace. + // Note that we can't just reflect the value of the gfxUserFontEntry's + // status, since the spec sometimes requires us to go through the event + // loop before updating the status, rather than doing it immediately. + FontFaceLoadStatus mStatus; + + // Represents where a FontFace's data is coming from. + enum SourceType { + eSourceType_FontFaceRule = 1, + eSourceType_URLs, + eSourceType_Buffer + }; + + // Where the font data for this FontFace is coming from. + SourceType mSourceType; + + // If the FontFace was constructed with an ArrayBuffer(View), this is a + // copy of the data from it. + RefPtr mBufferSource; + + // The values corresponding to the font face descriptors, if we are not + // a rule backed FontFace object. For rule backed objects, we use + // the descriptors stored in mRule. + // FIXME This should hold a unique ptr to just the descriptors inside, + // so that we don't need to create a rule for it and don't need to + // assign a fake line number and column number. See bug 1450904. + RefPtr mDescriptors; + + // The value of the unicode-range descriptor as a gfxCharacterMap. Valid + // only when mUnicodeRangeDirty is false. + RefPtr mUnicodeRange; + + // The primary FontFaceSet this FontFace is associated with, + // regardless of whether it is currently "in" the set. + RefPtr mFontFaceSet; + + // Other FontFaceSets (apart from mFontFaceSet) that this FontFace + // appears in. + nsTArray> mOtherFontFaceSets; + + // Whether mUnicodeRange needs to be rebuilt before being returned from + // GetUnicodeRangeAsCharacterMap. + bool mUnicodeRangeDirty; + + // Whether this FontFace appears in mFontFaceSet. + bool mInFontFaceSet; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFaceImpl_h) diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp new file mode 100644 index 0000000000..f350e5210a --- /dev/null +++ b/layout/style/FontFaceSet.cpp @@ -0,0 +1,487 @@ +/* -*- 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 "FontFaceSet.h" + +#include "gfxFontConstants.h" +#include "gfxFontSrcPrincipal.h" +#include "gfxFontSrcURI.h" +#include "gfxFontUtils.h" +#include "FontPreloader.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/CSSFontFaceRule.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/FontFaceImpl.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/dom/FontFaceSetDocumentImpl.h" +#include "mozilla/dom/FontFaceSetWorkerImpl.h" +#include "mozilla/dom/FontFaceSetIterator.h" +#include "mozilla/dom/FontFaceSetLoadEvent.h" +#include "mozilla/dom/FontFaceSetLoadEventBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/PresShellInlines.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSParser.h" +#include "mozilla/ServoStyleSet.h" +#include "mozilla/ServoUtils.h" +#include "mozilla/Sprintf.h" +#include "mozilla/Telemetry.h" +#include "mozilla/LoadInfo.h" +#include "nsComponentManagerUtils.h" +#include "nsContentPolicyUtils.h" +#include "nsContentUtils.h" +#include "nsDeviceContext.h" +#include "nsFontFaceLoader.h" +#include "nsIConsoleService.h" +#include "nsIContentPolicy.h" +#include "nsIDocShell.h" +#include "mozilla/dom/Document.h" +#include "nsILoadContext.h" +#include "nsINetworkPredictor.h" +#include "nsIPrincipal.h" +#include "nsIWebNavigation.h" +#include "nsNetUtil.h" +#include "nsIInputStream.h" +#include "nsLayoutUtils.h" +#include "nsPresContext.h" +#include "nsPrintfCString.h" +#include "nsUTF8Utils.h" +#include "nsDOMNavigationTiming.h" +#include "ReferrerInfo.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mImpl->GetDocument()); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady); + for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace); + } + for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, + DOMEventTargetHelper) + tmp->Destroy(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady); + for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace); + } + for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace); + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +FontFaceSet::FontFaceSet(nsIGlobalObject* aParent) + : DOMEventTargetHelper(aParent) {} + +FontFaceSet::~FontFaceSet() { + // Assert that we don't drop any FontFaceSet objects during a Servo traversal, + // since PostTraversalTask objects can hold raw pointers to FontFaceSets. + MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); + + Destroy(); +} + +/* static */ already_AddRefed FontFaceSet::CreateForDocument( + dom::Document* aDocument) { + RefPtr set = new FontFaceSet(aDocument->GetScopeObject()); + RefPtr impl = + new FontFaceSetDocumentImpl(set, aDocument); + set->mImpl = impl; + impl->Initialize(); + return set.forget(); +} + +/* static */ already_AddRefed FontFaceSet::CreateForWorker( + nsIGlobalObject* aParent, WorkerPrivate* aWorkerPrivate) { + RefPtr set = new FontFaceSet(aParent); + RefPtr impl = new FontFaceSetWorkerImpl(set); + set->mImpl = impl; + if (NS_WARN_IF(!impl->Initialize(aWorkerPrivate))) { + return nullptr; + } + return set.forget(); +} + +JSObject* FontFaceSet::WrapObject(JSContext* aContext, + JS::Handle aGivenProto) { + return FontFaceSet_Binding::Wrap(aContext, this, aGivenProto); +} + +void FontFaceSet::Destroy() { mImpl->Destroy(); } + +already_AddRefed FontFaceSet::Load(JSContext* aCx, + const nsACString& aFont, + const nsAString& aText, + ErrorResult& aRv) { + FlushUserFontSet(); + + nsTArray> promises; + + nsTArray faces; + mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv); + if (aRv.Failed()) { + return nullptr; + } + + for (FontFace* f : faces) { + RefPtr promise = f->Load(aRv); + if (aRv.Failed()) { + return nullptr; + } + if (!promises.AppendElement(promise, fallible)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + } + + return Promise::All(aCx, promises, aRv); +} + +bool FontFaceSet::Check(const nsACString& aFont, const nsAString& aText, + ErrorResult& aRv) { + FlushUserFontSet(); + + nsTArray faces; + mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv); + if (aRv.Failed()) { + return false; + } + + for (FontFace* f : faces) { + if (f->Status() != FontFaceLoadStatus::Loaded) { + return false; + } + } + + return true; +} + +bool FontFaceSet::ReadyPromiseIsPending() const { + return mReady ? mReady->State() == Promise::PromiseState::Pending + : !mResolveLazilyCreatedReadyPromise; +} + +Promise* FontFaceSet::GetReady(ErrorResult& aRv) { + mImpl->EnsureReady(); + + if (!mReady) { + nsCOMPtr global = GetParentObject(); + mReady = Promise::Create(global, aRv); + if (!mReady) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (mResolveLazilyCreatedReadyPromise) { + mReady->MaybeResolve(this); + mResolveLazilyCreatedReadyPromise = false; + } + } + + return mReady; +} + +FontFaceSetLoadStatus FontFaceSet::Status() { return mImpl->Status(); } + +#ifdef DEBUG +bool FontFaceSet::HasRuleFontFace(FontFace* aFontFace) { + for (size_t i = 0; i < mRuleFaces.Length(); i++) { + if (mRuleFaces[i].mFontFace == aFontFace) { + return true; + } + } + return false; +} +#endif + +void FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) { + FlushUserFontSet(); + + FontFaceImpl* fontImpl = aFontFace.GetImpl(); + MOZ_ASSERT(fontImpl); + + if (!mImpl->Add(fontImpl, aRv)) { + return; + } + + MOZ_ASSERT(!aRv.Failed()); + +#ifdef DEBUG + for (const FontFaceRecord& rec : mNonRuleFaces) { + MOZ_ASSERT(rec.mFontFace != &aFontFace, + "FontFace should not occur in mNonRuleFaces twice"); + } +#endif + + FontFaceRecord* rec = mNonRuleFaces.AppendElement(); + rec->mFontFace = &aFontFace; + rec->mOrigin = Nothing(); + rec->mLoadEventShouldFire = + fontImpl->Status() == FontFaceLoadStatus::Unloaded || + fontImpl->Status() == FontFaceLoadStatus::Loading; +} + +void FontFaceSet::Clear() { + nsTArray oldRecords = std::move(mNonRuleFaces); + mImpl->Clear(); +} + +bool FontFaceSet::Delete(FontFace& aFontFace) { + // Hold onto a strong reference to make sure that when we remove FontFace from + // the list, the FontFaceImpl does not get freed right away. We need to check + // the FontFaceSetImpl first. + RefPtr fontImpl = aFontFace.GetImpl(); + MOZ_ASSERT(fontImpl); + + // Ensure that we remove from mNonRuleFaces first. This is important so that + // when we check to see if all of the fonts have finished loading, the list in + // FontFaceSet and FontFaceSetImpl match. + bool removed = false; + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + if (mNonRuleFaces[i].mFontFace == &aFontFace) { + mNonRuleFaces.RemoveElementAt(i); + removed = true; + break; + } + } + + if (!mImpl->Delete(fontImpl)) { + MOZ_ASSERT(!removed, "Missing rule present in Impl!"); + } else { + MOZ_ASSERT(removed, "Rule present but missing in Impl!"); + } + + return removed; +} + +bool FontFaceSet::HasAvailableFontFace(FontFace* aFontFace) { + return aFontFace->GetImpl()->IsInFontFaceSet(mImpl); +} + +bool FontFaceSet::Has(FontFace& aFontFace) { + FlushUserFontSet(); + + return HasAvailableFontFace(&aFontFace); +} + +FontFace* FontFaceSet::GetFontFaceAt(uint32_t aIndex) { + FlushUserFontSet(); + + if (aIndex < mRuleFaces.Length()) { + auto& entry = mRuleFaces[aIndex]; + if (entry.mOrigin.value() != StyleOrigin::Author) { + return nullptr; + } + return entry.mFontFace; + } + + aIndex -= mRuleFaces.Length(); + if (aIndex < mNonRuleFaces.Length()) { + return mNonRuleFaces[aIndex].mFontFace; + } + + return nullptr; +} + +uint32_t FontFaceSet::Size() { + FlushUserFontSet(); + + // Web IDL objects can only expose array index properties up to INT32_MAX. + + size_t total = mNonRuleFaces.Length(); + for (const auto& entry : mRuleFaces) { + if (entry.mOrigin.value() == StyleOrigin::Author) { + ++total; + } + } + return std::min(total, INT32_MAX); +} + +uint32_t FontFaceSet::SizeIncludingNonAuthorOrigins() { + FlushUserFontSet(); + + // Web IDL objects can only expose array index properties up to INT32_MAX. + + size_t total = mRuleFaces.Length() + mNonRuleFaces.Length(); + return std::min(total, INT32_MAX); +} + +already_AddRefed FontFaceSet::Entries() { + RefPtr it = new FontFaceSetIterator(this, true); + return it.forget(); +} + +already_AddRefed FontFaceSet::Values() { + RefPtr it = new FontFaceSetIterator(this, false); + return it.forget(); +} + +void FontFaceSet::ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, + JS::Handle aThisArg, ErrorResult& aRv) { + JS::Rooted thisArg(aCx, aThisArg); + for (size_t i = 0; i < SizeIncludingNonAuthorOrigins(); i++) { + RefPtr face = GetFontFaceAt(i); + if (!face) { + // The font at index |i| is a non-Author origin font, which we shouldn't + // expose per spec. + continue; + } + aCallback.Call(thisArg, *face, *face, *this, aRv); + if (aRv.Failed()) { + return; + } + } +} + +bool FontFaceSet::UpdateRules(const nsTArray& aRules) { + // The impl object handles the callbacks for recreating the mRulesFaces array. + nsTArray oldRecords = std::move(mRuleFaces); + return mImpl->UpdateRules(aRules); +} + +void FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin) { + MOZ_ASSERT(!HasRuleFontFace(aFontFace)); + + FontFaceRecord* rec = mRuleFaces.AppendElement(); + rec->mFontFace = aFontFace; + rec->mOrigin = Some(aOrigin); + rec->mLoadEventShouldFire = + aFontFace->Status() == FontFaceLoadStatus::Unloaded || + aFontFace->Status() == FontFaceLoadStatus::Loading; +} + +void FontFaceSet::DidRefresh() { mImpl->CheckLoadingFinished(); } + +void FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise() { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + + if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { + // See comments in Gecko_GetFontMetrics. + // + // We can't just dispatch the runnable below if we're not on the main + // thread, since it needs to take a strong reference to the FontFaceSet, + // and being a DOM object, FontFaceSet doesn't support thread-safe + // refcounting. (Also, the Promise object creation must be done on + // the main thread.) + set->AppendTask( + PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this)); + return; + } + + (new AsyncEventDispatcher(this, u"loading"_ns, CanBubble::eNo)) + ->PostDOMEvent(); + + if (mReady && mReady->State() != Promise::PromiseState::Pending) { + if (GetParentObject()) { + ErrorResult rv; + mReady = Promise::Create(GetParentObject(), rv); + } + } + + // We may previously have been in a state where all fonts had finished + // loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that + // if we lazily create mReady for a consumer that we resolve it before + // returning it. We're now loading fonts, so we need to clear that flag. + mResolveLazilyCreatedReadyPromise = false; +} + +void FontFaceSet::MaybeResolve() { + if (mReady) { + mReady->MaybeResolve(this); + } else { + mResolveLazilyCreatedReadyPromise = true; + } + + // Now dispatch the loadingdone/loadingerror events. + nsTArray> loaded; + nsTArray> failed; + + auto checkStatus = [&](nsTArray& faces) -> void { + for (auto& face : faces) { + if (!face.mLoadEventShouldFire) { + continue; + } + FontFace* f = face.mFontFace; + switch (f->Status()) { + case FontFaceLoadStatus::Unloaded: + break; + case FontFaceLoadStatus::Loaded: + loaded.AppendElement(*f); + face.mLoadEventShouldFire = false; + break; + case FontFaceLoadStatus::Error: + failed.AppendElement(*f); + face.mLoadEventShouldFire = false; + break; + case FontFaceLoadStatus::Loading: + // We should've returned above at MightHavePendingFontLoads()! + case FontFaceLoadStatus::EndGuard_: + MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus"); + break; + } + } + }; + + checkStatus(mRuleFaces); + checkStatus(mNonRuleFaces); + + DispatchLoadingFinishedEvent(u"loadingdone"_ns, std::move(loaded)); + + if (!failed.IsEmpty()) { + DispatchLoadingFinishedEvent(u"loadingerror"_ns, std::move(failed)); + } +} + +void FontFaceSet::DispatchLoadingFinishedEvent( + const nsAString& aType, nsTArray>&& aFontFaces) { + FontFaceSetLoadEventInit init; + init.mBubbles = false; + init.mCancelable = false; + init.mFontfaces = std::move(aFontFaces); + RefPtr event = + FontFaceSetLoadEvent::Constructor(this, aType, init); + (new AsyncEventDispatcher(this, event.forget()))->PostDOMEvent(); +} + +void FontFaceSet::FlushUserFontSet() { mImpl->FlushUserFontSet(); } + +void FontFaceSet::RefreshStandardFontLoadPrincipal() { + MOZ_ASSERT(NS_IsMainThread()); + mImpl->RefreshStandardFontLoadPrincipal(); +} + +void FontFaceSet::CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const { + for (const FontFaceRecord& rec : mNonRuleFaces) { + IgnoredErrorResult rv; + RefPtr f = rec.mFontFace; + aFontFaceSet->Add(*f, rv); + MOZ_ASSERT(!rv.Failed()); + } +} + +#undef LOG_ENABLED +#undef LOG diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h new file mode 100644 index 0000000000..35c22ca0f9 --- /dev/null +++ b/layout/style/FontFaceSet.h @@ -0,0 +1,174 @@ +/* -*- 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_dom_FontFaceSet_h +#define mozilla_dom_FontFaceSet_h + +#include "mozilla/dom/FontFace.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/dom/FontFaceSetImpl.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "nsICSSLoaderObserver.h" +#include "nsIDOMEventListener.h" + +class nsFontFaceLoader; +class nsIPrincipal; +class nsIGlobalObject; + +namespace mozilla { +class PostTraversalTask; +class SharedFontList; +namespace dom { +class Promise; +class WorkerPrivate; +} // namespace dom +} // namespace mozilla + +namespace mozilla::dom { + +class FontFaceSet final : public DOMEventTargetHelper { + friend class mozilla::PostTraversalTask; + + public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper) + + static already_AddRefed CreateForDocument( + dom::Document* aDocument); + + static already_AddRefed CreateForWorker( + nsIGlobalObject* aParent, WorkerPrivate* aWorkerPrivate); + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + bool UpdateRules(const nsTArray& aRules); + + /** + * Notification method called by the nsPresContext to indicate that the + * refresh driver ticked and flushed style and layout. + * were just flushed. + */ + void DidRefresh(); + + void FlushUserFontSet(); + + void RefreshStandardFontLoadPrincipal(); + + void CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const; + + void CacheFontLoadability() { mImpl->CacheFontLoadability(); } + + FontFaceSetImpl* GetImpl() const { return mImpl; } + + // -- Web IDL -------------------------------------------------------------- + + IMPL_EVENT_HANDLER(loading) + IMPL_EVENT_HANDLER(loadingdone) + IMPL_EVENT_HANDLER(loadingerror) + already_AddRefed Load(JSContext* aCx, const nsACString& aFont, + const nsAString& aText, ErrorResult& aRv); + bool Check(const nsACString& aFont, const nsAString& aText, ErrorResult& aRv); + dom::Promise* GetReady(ErrorResult& aRv); + dom::FontFaceSetLoadStatus Status(); + + void Add(FontFace& aFontFace, ErrorResult& aRv); + void Clear(); + bool Delete(FontFace& aFontFace); + bool Has(FontFace& aFontFace); + /** + * This returns the number of Author origin fonts only. + * (see also SizeIncludingNonAuthorOrigins() below) + */ + uint32_t Size(); + already_AddRefed Entries(); + already_AddRefed Values(); + MOZ_CAN_RUN_SCRIPT + void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, + JS::Handle aThisArg, ErrorResult& aRv); + + /** + * Unlike Size(), this returns the size including non-Author origin fonts. + */ + uint32_t SizeIncludingNonAuthorOrigins(); + + void MaybeResolve(); + + void DispatchLoadingFinishedEvent( + const nsAString& aType, nsTArray>&& aFontFaces); + + void DispatchLoadingEventAndReplaceReadyPromise(); + void DispatchCheckLoadingFinishedAfterDelay(); + + // Whether mReady is pending, or would be when created. + bool ReadyPromiseIsPending() const; + + void InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin); + + private: + friend mozilla::dom::FontFaceSetIterator; // needs GetFontFaceAt() + + explicit FontFaceSet(nsIGlobalObject* aParent); + ~FontFaceSet(); + + /** + * Returns whether the given FontFace is currently "in" the FontFaceSet. + */ + bool HasAvailableFontFace(FontFace* aFontFace); + + /** + * Removes any listeners and observers. + */ + void Destroy(); + + /** + * Returns the font at aIndex if it's an Author origin font, or nullptr + * otherwise. + */ + FontFace* GetFontFaceAt(uint32_t aIndex); + + // Note: if you add new cycle collected objects to FontFaceRecord, + // make sure to update FontFaceSet's cycle collection macros + // accordingly. + struct FontFaceRecord { + RefPtr mFontFace; + Maybe mOrigin; // only relevant for mRuleFaces entries + + // When true, indicates that when finished loading, the FontFace should be + // included in the subsequent loadingdone/loadingerror event fired at the + // FontFaceSet. + bool mLoadEventShouldFire; + }; + +#ifdef DEBUG + bool HasRuleFontFace(FontFace* aFontFace); +#endif + + // The underlying implementation for FontFaceSet. + RefPtr mImpl; + + // A Promise that is fulfilled once all of the FontFace objects + // in mRuleFaces and mNonRuleFaces that started or were loading at the + // time the Promise was created have finished loading. It is rejected if + // any of those fonts failed to load. mReady is replaced with + // a new Promise object whenever mReady is settled and another + // FontFace in mRuleFaces or mNonRuleFaces starts to load. + // Note that mReady is created lazily when GetReady() is called. + RefPtr mReady; + // Whether the ready promise must be resolved when it's created. + bool mResolveLazilyCreatedReadyPromise = false; + + // The @font-face rule backed FontFace objects in the FontFaceSet. + nsTArray mRuleFaces; + + // The non rule backed FontFace objects that have been added to this + // FontFaceSet. + nsTArray mNonRuleFaces; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFaceSet_h) diff --git a/layout/style/FontFaceSetDocumentImpl.cpp b/layout/style/FontFaceSetDocumentImpl.cpp new file mode 100644 index 0000000000..33f7404c7c --- /dev/null +++ b/layout/style/FontFaceSetDocumentImpl.cpp @@ -0,0 +1,763 @@ +/* -*- 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 "FontFaceSetDocumentImpl.h" +#include "mozilla/FontLoaderUtils.h" +#include "mozilla/LoadInfo.h" +#include "mozilla/PresShell.h" +#include "mozilla/PresShellInlines.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/FontFaceImpl.h" +#include "mozilla/dom/FontFaceSet.h" +#include "nsContentPolicyUtils.h" +#include "nsDOMNavigationTiming.h" +#include "nsFontFaceLoader.h" +#include "nsIDocShell.h" +#include "nsINetworkPredictor.h" +#include "nsISupportsPriority.h" +#include "nsIWebNavigation.h" +#include "nsPresContext.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +#define LOG(args) \ + MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug) + +NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl, + nsIDOMEventListener, nsICSSLoaderObserver) + +FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner, + dom::Document* aDocument) + : FontFaceSetImpl(aOwner), mDocument(aDocument) {} + +FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default; + +void FontFaceSetDocumentImpl::Initialize() { + RecursiveMutexAutoLock lock(mMutex); + + MOZ_ASSERT(mDocument, "We should get a valid document from the caller!"); + + // Record the state of the "bypass cache" flags from the docshell now, + // since we want to look at them from style worker threads, and we can + // only get to the docshell through a weak pointer (which is only + // possible on the main thread). + // + // In theory the load type of a docshell could change after the document + // is loaded, but handling that doesn't seem too important. + if (nsCOMPtr docShell = mDocument->GetDocShell()) { + uint32_t loadType; + uint32_t flags; + if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) && + ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) || + (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) && + (flags & nsIRequest::LOAD_BYPASS_CACHE))) { + mBypassCache = true; + } + } + + // Same for the "private browsing" flag. + if (nsCOMPtr loadContext = mDocument->GetLoadContext()) { + mPrivateBrowsing = loadContext->UsePrivateBrowsing(); + } + + if (!mDocument->DidFireDOMContentLoaded()) { + mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false, + false); + } else { + // In some cases we can't rely on CheckLoadingFinished being called from + // the refresh driver. For example, documents in display:none iframes. + // Or if the document has finished loading and painting at the time that + // script requests document.fonts and causes us to get here. + CheckLoadingFinished(); + } + + mDocument->CSSLoader()->AddObserver(this); + + mStandardFontLoadPrincipal = MakeRefPtr( + mDocument->NodePrincipal(), mDocument->PartitionedPrincipal()); +} + +void FontFaceSetDocumentImpl::Destroy() { + RemoveDOMContentLoadedListener(); + + if (mDocument && mDocument->CSSLoader()) { + // We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might + // be being called during unlink, at which time the loader may already have + // been unlinked from the document. + mDocument->CSSLoader()->RemoveObserver(this); + } + + mRuleFaces.Clear(); + + // Since the base class may depend on the document remaining set, we need to + // clear mDocument after. + FontFaceSetImpl::Destroy(); + + mDocument = nullptr; +} + +bool FontFaceSetDocumentImpl::IsOnOwningThread() { return NS_IsMainThread(); } + +#ifdef DEBUG +void FontFaceSetDocumentImpl::AssertIsOnOwningThread() { + MOZ_ASSERT(NS_IsMainThread()); +} +#endif + +void FontFaceSetDocumentImpl::DispatchToOwningThread( + const char* aName, std::function&& aFunc) { + class FontFaceSetDocumentRunnable final : public Runnable { + public: + FontFaceSetDocumentRunnable(const char* aName, + std::function&& aFunc) + : Runnable(aName), mFunc(std::move(aFunc)) {} + + NS_IMETHOD Run() final { + mFunc(); + return NS_OK; + } + + private: + std::function mFunc; + }; + + RefPtr runnable = + new FontFaceSetDocumentRunnable(aName, std::move(aFunc)); + NS_DispatchToMainThread(runnable.forget()); +} + +uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() { + MOZ_ASSERT(NS_IsMainThread()); + if (!mDocument) { + return 0; + } + + return mDocument->InnerWindowID(); +} + +nsPresContext* FontFaceSetDocumentImpl::GetPresContext() const { + mozilla::AssertIsMainThreadOrServoFontMetricsLocked(); + if (!mDocument) { + return nullptr; + } + + return mDocument->GetPresContext(); +} + +void FontFaceSetDocumentImpl::RefreshStandardFontLoadPrincipal() { + MOZ_ASSERT(NS_IsMainThread()); + RecursiveMutexAutoLock lock(mMutex); + if (NS_WARN_IF(!mDocument)) { + return; + } + mStandardFontLoadPrincipal = MakeRefPtr( + mDocument->NodePrincipal(), mDocument->PartitionedPrincipal()); + FontFaceSetImpl::RefreshStandardFontLoadPrincipal(); +} + +already_AddRefed FontFaceSetDocumentImpl::GetURLExtraData() { + if (!mDocument) { + return nullptr; + } + return do_AddRef(mDocument->DefaultStyleAttrURLData()); +} + +void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() { + if (mDocument) { + mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false); + } +} + +void FontFaceSetDocumentImpl::FindMatchingFontFaces( + const nsTHashSet& aMatchingFaces, + nsTArray& aFontFaces) { + FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces); + for (FontFaceRecord& record : mRuleFaces) { + FontFace* owner = record.mFontFace->GetOwner(); + if (owner && aMatchingFaces.Contains(owner)) { + aFontFaces.AppendElement(owner); + } + } +} + +TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() { + TimeStamp navStart; + RefPtr timing(mDocument->GetNavigationTiming()); + if (timing) { + navStart = timing->GetNavigationStartTimeStamp(); + } + return navStart; +} + +void FontFaceSetDocumentImpl::EnsureReady() { + MOZ_ASSERT(NS_IsMainThread()); + + // There may be outstanding style changes that will trigger the loading of + // new fonts. We need to flush layout to initiate any such loads so that + // if mReady is currently resolved we replace it with a new pending Promise. + // (That replacement will happen under this flush call.) + if (!ReadyPromiseIsPending() && mDocument) { + mDocument->FlushPendingNotifications(FlushType::Layout); + } +} + +#ifdef DEBUG +bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) { + for (size_t i = 0; i < mRuleFaces.Length(); i++) { + if (mRuleFaces[i].mFontFace == aFontFace) { + return true; + } + } + return false; +} +#endif + +bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) { + if (NS_WARN_IF(!mDocument)) { + return false; + } + + if (!FontFaceSetImpl::Add(aFontFace, aRv)) { + return false; + } + + RefPtr clonedDoc = mDocument->GetLatestStaticClone(); + if (clonedDoc) { + // The document is printing, copy the font to the static clone as well. + nsCOMPtr principal = mDocument->GetPrincipal(); + if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) { + ErrorResult rv; + clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv); + MOZ_ASSERT(!rv.Failed()); + } + } + + return true; +} + +nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry, + uint32_t aSrcIndex) { + if (NS_WARN_IF(!mDocument)) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + + nsCOMPtr streamLoader; + RefPtr fontLoader; + + const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex); + + auto preloadKey = + PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS); + RefPtr preload = + mDocument->Preloads().LookupPreload(preloadKey); + + if (preload) { + fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, + preload->Channel()); + rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, + fontLoader); + NS_ENSURE_SUCCESS(rv, rv); + + rv = preload->AsyncConsume(streamLoader); + + // We don't want this to hang around regardless of the result, there will be + // no coalescing of later found tags for fonts. + preload->RemoveSelf(mDocument); + } else { + // No preload found, open a channel. + rv = NS_ERROR_FAILURE; + } + + nsCOMPtr loadGroup(mDocument->GetDocumentLoadGroup()); + if (NS_FAILED(rv)) { + nsCOMPtr channel; + rv = FontLoaderUtils::BuildChannel( + getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS, + dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src, + mDocument, loadGroup, nullptr, false, + nsISupportsPriority::PRIORITY_HIGH); + NS_ENSURE_SUCCESS(rv, rv); + + fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel); + + if (LOG_ENABLED()) { + nsCOMPtr referrer = src.mReferrerInfo + ? src.mReferrerInfo->GetOriginalReferrer() + : nullptr; + LOG(( + "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n", + fontLoader.get(), src.mURI->GetSpecOrDefault().get(), + referrer ? referrer->GetSpecOrDefault().get() : "")); + } + + rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, + fontLoader); + NS_ENSURE_SUCCESS(rv, rv); + + rv = channel->AsyncOpen(streamLoader); + if (NS_FAILED(rv)) { + fontLoader->DropChannel(); // explicitly need to break ref cycle + } + } + + { + RecursiveMutexAutoLock lock(mMutex); + mLoaders.PutEntry(fontLoader); + } + + net::PredictorLearn(src.mURI->get(), mDocument->GetDocumentURI(), + nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup); + + if (NS_SUCCEEDED(rv)) { + fontLoader->StartedLoading(streamLoader); + // let the font entry remember the loader, in case we need to cancel it + aUserFontEntry->SetLoader(fontLoader); + } + + return rv; +} + +bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { + MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL); + + if (ServoStyleSet::IsInServoTraversal()) { + RecursiveMutexAutoLock lock(mMutex); + auto entry = mAllowedFontLoads.Lookup(&aSrc); + MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?"); + return entry ? *entry : false; + } + + MOZ_ASSERT(NS_IsMainThread()); + + if (aSrc.mUseOriginPrincipal) { + return true; + } + + if (NS_WARN_IF(!mDocument)) { + return false; + } + + RefPtr gfxPrincipal = + aSrc.mURI->InheritsSecurityContext() ? nullptr + : aSrc.LoadPrincipal(*this); + + nsIPrincipal* principal = + gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr; + + nsCOMPtr secCheckLoadInfo = new net::LoadInfo( + mDocument->NodePrincipal(), // loading principal + principal, // triggering principal + mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, + nsIContentPolicy::TYPE_FONT); + + int16_t shouldLoad = nsIContentPolicy::ACCEPT; + nsresult rv = + NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad, + nsContentUtils::GetContentPolicy()); + + return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); +} + +nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData( + nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc) { + gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal(); + + // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a + // node and a principal. This is because the document where the font is + // being loaded might have a different origin from the principal of the + // stylesheet that initiated the font load. + // Further, we only get here for data: loads, so it doesn't really matter + // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be + // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT. + return NS_NewChannelWithTriggeringPrincipal( + aOutChannel, aFontFaceSrc->mURI->get(), mDocument, + principal ? principal->NodePrincipal() : nullptr, + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT + : nsIContentPolicy::TYPE_FONT); +} + +bool FontFaceSetDocumentImpl::UpdateRules( + const nsTArray& aRules) { + RecursiveMutexAutoLock lock(mMutex); + + // If there was a change to the mNonRuleFaces array, then there could + // have been a modification to the user font set. + bool modified = mNonRuleFacesDirty; + mNonRuleFacesDirty = false; + + // reuse existing FontFace objects mapped to rules already + nsTHashMap, FontFaceImpl*> ruleFaceMap; + for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) { + FontFaceImpl* f = mRuleFaces[i].mFontFace; + if (!f || !f->GetOwner()) { + continue; + } + ruleFaceMap.InsertOrUpdate(f->GetRule(), f); + } + + // The @font-face rules that make up the user font set have changed, + // so we need to update the set. However, we want to preserve existing + // font entries wherever possible, so that we don't discard and then + // re-download resources in the (common) case where at least some of the + // same rules are still present. + + nsTArray oldRecords = std::move(mRuleFaces); + + // Remove faces from the font family records; we need to re-insert them + // because we might end up with faces in a different order even if they're + // the same font entries as before. (The order can affect font selection + // where multiple faces match the requested style, perhaps with overlapping + // unicode-range coverage.) + for (const auto& fontFamily : mFontFamilies.Values()) { + fontFamily->DetachFontEntries(); + } + + // Sometimes aRules has duplicate @font-face rules in it; we should make + // that not happen, but in the meantime, don't try to insert the same + // FontFace object more than once into mRuleFaces. We track which + // ones we've handled in this table. + nsTHashSet handledRules; + + for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) { + // Insert each FontFace objects for each rule into our list, migrating old + // font entries if possible rather than creating new ones; set modified to + // true if we detect that rule ordering has changed, or if a new entry is + // created. + StyleLockedFontFaceRule* rule = aRules[i].mRule; + if (!handledRules.EnsureInserted(rule)) { + // rule was already present in the hashtable + continue; + } + RefPtr faceImpl = ruleFaceMap.Get(rule); + RefPtr face = faceImpl ? faceImpl->GetOwner() : nullptr; + if (mOwner && (!faceImpl || !face)) { + face = FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, rule); + faceImpl = face->GetImpl(); + } + InsertRuleFontFace(faceImpl, face, aRules[i].mOrigin, oldRecords, modified); + } + + for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) { + // Do the same for the non rule backed FontFace objects. + InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace); + } + + // Remove any residual families that have no font entries (i.e., they were + // not defined at all by the updated set of @font-face rules). + for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) { + if (!it.Data()->FontListLength()) { + it.Remove(); + } + } + + // If any FontFace objects for rules are left in the old list, note that the + // set has changed (even if the new set was built entirely by migrating old + // font entries). + if (oldRecords.Length() > 0) { + modified = true; + // Any in-progress loaders for obsolete rules should be cancelled, + // as the resource being downloaded will no longer be required. + // We need to explicitly remove any loaders here, otherwise the loaders + // will keep their "orphaned" font entries alive until they complete, + // even after the oldRules array is deleted. + // + // XXX Now that it is possible for the author to hold on to a rule backed + // FontFace object, we shouldn't cancel loading here; instead we should do + // it when the FontFace is GCed, if we can detect that. + size_t count = oldRecords.Length(); + for (size_t i = 0; i < count; ++i) { + RefPtr f = oldRecords[i].mFontFace; + gfxUserFontEntry* userFontEntry = f->GetUserFontEntry(); + if (userFontEntry) { + nsFontFaceLoader* loader = userFontEntry->GetLoader(); + if (loader) { + loader->Cancel(); + RemoveLoader(loader); + } + } + + // Any left over FontFace objects should also cease being rule backed. + f->DisconnectFromRule(); + } + } + + if (modified) { + IncrementGeneration(true); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingStarted(); + CheckLoadingFinished(); + } + + // if local rules needed to be rebuilt, they have been rebuilt at this point + if (mRebuildLocalRules) { + mLocalRulesUsed = false; + mRebuildLocalRules = false; + } + + if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) { + LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this, + (modified ? "modified" : "not modified"), (int)(mRuleFaces.Length()))); + } + + return modified; +} + +void FontFaceSetDocumentImpl::InsertRuleFontFace( + FontFaceImpl* aFontFace, FontFace* aFontFaceOwner, StyleOrigin aSheetType, + nsTArray& aOldRecords, bool& aFontSetModified) { + RecursiveMutexAutoLock lock(mMutex); + + gfxUserFontAttributes attr; + if (!aFontFace->GetAttributes(attr)) { + // If there is no family name, this rule cannot contribute a + // usable font, so there is no point in processing it further. + return; + } + + bool remove = false; + size_t removeIndex; + + // This is a rule backed FontFace. First, we check in aOldRecords; if + // the FontFace for the rule exists there, just move it to the new record + // list, and put the entry into the appropriate family. + for (size_t i = 0; i < aOldRecords.Length(); ++i) { + FontFaceRecord& rec = aOldRecords[i]; + + if (rec.mFontFace == aFontFace && rec.mOrigin == Some(aSheetType)) { + // if local rules were used, don't use the old font entry + // for rules containing src local usage + if (mLocalRulesUsed && mRebuildLocalRules) { + if (aFontFace->HasLocalSrc()) { + // Remove the old record, but wait to see if we successfully create a + // new user font entry below. + remove = true; + removeIndex = i; + break; + } + } + + gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry(); + MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now"); + + AddUserFontEntry(attr.mFamilyName, entry); + + MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace), + "FontFace should not occur in mRuleFaces twice"); + + mRuleFaces.AppendElement(rec); + aOldRecords.RemoveElementAt(i); + + if (mOwner && aFontFaceOwner) { + mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType); + } + + // note the set has been modified if an old rule was skipped to find + // this one - something has been dropped, or ordering changed + if (i > 0) { + aFontSetModified = true; + } + return; + } + } + + // this is a new rule: + nsAutoCString family(attr.mFamilyName); + RefPtr entry = FindOrCreateUserFontEntryFromFontFace( + aFontFace, std::move(attr), aSheetType); + + if (!entry) { + return; + } + + if (remove) { + // Although we broke out of the aOldRecords loop above, since we found + // src local usage, and we're not using the old user font entry, we still + // are adding a record to mRuleFaces with the same FontFace object. + // Remove the old record so that we don't have the same FontFace listed + // in both mRuleFaces and oldRecords, which would cause us to call + // DisconnectFromRule on a FontFace that should still be rule backed. + aOldRecords.RemoveElementAt(removeIndex); + } + + FontFaceRecord rec; + rec.mFontFace = aFontFace; + rec.mOrigin = Some(aSheetType); + + aFontFace->SetUserFontEntry(entry); + + MOZ_ASSERT(!HasRuleFontFace(aFontFace), + "FontFace should not occur in mRuleFaces twice"); + + mRuleFaces.AppendElement(rec); + + if (mOwner && aFontFaceOwner) { + mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType); + } + + // this was a new rule and font entry, so note that the set was modified + aFontSetModified = true; + + // Add the entry to the end of the list. If an existing userfont entry was + // returned by FindOrCreateUserFontEntryFromFontFace that was already stored + // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry + // calls, will automatically remove the earlier occurrence of the same + // userfont entry. + AddUserFontEntry(family, entry); +} + +StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry( + gfxFontEntry* aFontEntry) { + NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries"); + for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { + FontFaceImpl* f = mRuleFaces[i].mFontFace; + gfxUserFontEntry* entry = f->GetUserFontEntry(); + if (entry && entry->GetPlatformFontEntry() == aFontEntry) { + return f->GetRule(); + } + } + return nullptr; +} + +StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry( + gfxUserFontEntry* aUserFontEntry) { + for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { + FontFaceImpl* f = mRuleFaces[i].mFontFace; + if (f->GetUserFontEntry() == aUserFontEntry) { + return f->GetRule(); + } + } + return nullptr; +} + +void FontFaceSetDocumentImpl::CacheFontLoadability() { + RecursiveMutexAutoLock lock(mMutex); + + // TODO(emilio): We could do it a bit more incrementally maybe? + for (const auto& fontFamily : mFontFamilies.Values()) { + fontFamily->ReadLock(); + for (const gfxFontEntry* entry : fontFamily->GetFontList()) { + if (!entry->mIsUserFontContainer) { + continue; + } + + const auto& sourceList = + static_cast(entry)->SourceList(); + for (const gfxFontFaceSrc& src : sourceList) { + if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) { + continue; + } + mAllowedFontLoads.LookupOrInsertWith( + &src, [&] { return IsFontLoadAllowed(src); }); + } + } + fontFamily->ReadUnlock(); + } +} + +void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); } + +void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() { + RecursiveMutexAutoLock lock(mMutex); + FontFaceSetImpl::UpdateHasLoadingFontFaces(); + + if (mHasLoadingFontFaces) { + return; + } + + for (size_t i = 0; i < mRuleFaces.Length(); i++) { + FontFaceImpl* f = mRuleFaces[i].mFontFace; + if (f->Status() == FontFaceLoadStatus::Loading) { + mHasLoadingFontFaces = true; + return; + } + } +} + +bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() { + if (FontFaceSetImpl::MightHavePendingFontLoads()) { + return true; + } + + // Check for pending restyles or reflows, as they might cause fonts to + // load as new styles apply and text runs are rebuilt. + nsPresContext* presContext = GetPresContext(); + if (presContext && presContext->HasPendingRestyleOrReflow()) { + return true; + } + + if (mDocument) { + // We defer resolving mReady until the document as fully loaded. + if (!mDocument->DidFireDOMContentLoaded()) { + return true; + } + + // And we also wait for any CSS style sheets to finish loading, as their + // styles might cause new fonts to load. + if (mDocument->CSSLoader()->HasPendingLoads()) { + return true; + } + } + + return false; +} + +// nsIDOMEventListener + +NS_IMETHODIMP +FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) { + nsString type; + aEvent->GetType(type); + + if (!type.EqualsLiteral("DOMContentLoaded")) { + return NS_ERROR_FAILURE; + } + + RemoveDOMContentLoadedListener(); + CheckLoadingFinished(); + + return NS_OK; +} + +// nsICSSLoaderObserver + +NS_IMETHODIMP +FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, + nsresult aStatus) { + CheckLoadingFinished(); + return NS_OK; +} + +void FontFaceSetDocumentImpl::FlushUserFontSet() { + if (mDocument) { + mDocument->FlushUserFontSet(); + } +} + +void FontFaceSetDocumentImpl::MarkUserFontSetDirty() { + if (mDocument) { + // Ensure we trigger at least a style flush, that will eventually flush the + // user font set. Otherwise the font loads that that flush may cause could + // never be triggered. + if (PresShell* presShell = mDocument->GetPresShell()) { + presShell->EnsureStyleFlush(); + } + mDocument->MarkUserFontSetDirty(); + } +} + +#undef LOG_ENABLED +#undef LOG diff --git a/layout/style/FontFaceSetDocumentImpl.h b/layout/style/FontFaceSetDocumentImpl.h new file mode 100644 index 0000000000..d32103e203 --- /dev/null +++ b/layout/style/FontFaceSetDocumentImpl.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 http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FontFaceSetDocumentImpl_h +#define mozilla_dom_FontFaceSetDocumentImpl_h + +#include "mozilla/dom/FontFaceSetImpl.h" +#include "nsICSSLoaderObserver.h" +#include "nsIDOMEventListener.h" + +namespace mozilla::dom { + +class FontFaceSetDocumentImpl final : public FontFaceSetImpl, + public nsIDOMEventListener, + public nsICSSLoaderObserver { + NS_DECL_ISUPPORTS_INHERITED + + public: + NS_DECL_NSIDOMEVENTLISTENER + + FontFaceSetDocumentImpl(FontFaceSet* aOwner, dom::Document* aDocument); + + void Initialize(); + void Destroy() override; + + bool IsOnOwningThread() override; +#ifdef DEBUG + void AssertIsOnOwningThread() override; +#endif + void DispatchToOwningThread(const char* aName, + std::function&& aFunc) override; + + void RefreshStandardFontLoadPrincipal() override; + + dom::Document* GetDocument() const override { return mDocument; } + + already_AddRefed GetURLExtraData() override; + + // gfxUserFontSet + + nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, + uint32_t aSrcIndex) override; + + bool IsFontLoadAllowed(const gfxFontFaceSrc&) override; + + nsPresContext* GetPresContext() const override; + + bool UpdateRules(const nsTArray& aRules) override; + + StyleLockedFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry) override; + + /** + * Notification method called by the nsPresContext to indicate that the + * refresh driver ticked and flushed style and layout. + * were just flushed. + */ + void DidRefresh() override; + + // nsICSSLoaderObserver + NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, + nsresult aStatus) override; + + // For ServoStyleSet to know ahead of time whether a font is loadable. + void CacheFontLoadability() override; + + void EnsureReady() override; + + bool Add(FontFaceImpl* aFontFace, ErrorResult& aRv) override; + + void FlushUserFontSet() override; + void MarkUserFontSetDirty() override; + + private: + ~FontFaceSetDocumentImpl() override; + + uint64_t GetInnerWindowID() override; + + void RemoveDOMContentLoadedListener(); + + nsresult CreateChannelForSyncLoadFontData( + nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc) override; + + // search for @font-face rule that matches a userfont font entry + StyleLockedFontFaceRule* FindRuleForUserFontEntry( + gfxUserFontEntry* aUserFontEntry) override; + + void FindMatchingFontFaces(const nsTHashSet& aMatchingFaces, + nsTArray& aFontFaces) override; + + void InsertRuleFontFace(FontFaceImpl* aFontFace, FontFace* aFontFaceOwner, + StyleOrigin aOrigin, + nsTArray& aOldRecords, + bool& aFontSetModified); + + // Helper function for HasLoadingFontFaces. + void UpdateHasLoadingFontFaces() override; + + /** + * Returns whether there might be any pending font loads, which should cause + * the mReady Promise not to be resolved yet. + */ + bool MightHavePendingFontLoads() override; + +#ifdef DEBUG + bool HasRuleFontFace(FontFaceImpl* aFontFace); +#endif + + TimeStamp GetNavigationStartTimeStamp() override; + + // The document this is a FontFaceSet for. + RefPtr mDocument; + + // The @font-face rule backed FontFace objects in the FontFaceSet. + nsTArray mRuleFaces; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFaceSetDocumentImpl_h) diff --git a/layout/style/FontFaceSetImpl.cpp b/layout/style/FontFaceSetImpl.cpp new file mode 100644 index 0000000000..7383842a41 --- /dev/null +++ b/layout/style/FontFaceSetImpl.cpp @@ -0,0 +1,954 @@ +/* -*- 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 "FontFaceSetImpl.h" + +#include "gfxFontConstants.h" +#include "gfxFontSrcPrincipal.h" +#include "gfxFontSrcURI.h" +#include "gfxFontUtils.h" +#include "gfxPlatformFontList.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/CSSFontFaceRule.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/FontFaceImpl.h" +#include "mozilla/dom/FontFaceSet.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/dom/FontFaceSetLoadEvent.h" +#include "mozilla/dom/FontFaceSetLoadEventBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerRunnable.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/glean/GleanMetrics.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/PresShellInlines.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoCSSParser.h" +#include "mozilla/ServoStyleSet.h" +#include "mozilla/ServoUtils.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/Telemetry.h" +#include "mozilla/LoadInfo.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" +#include "nsDeviceContext.h" +#include "nsFontFaceLoader.h" +#include "nsIConsoleService.h" +#include "nsIContentPolicy.h" +#include "nsIDocShell.h" +#include "nsILoadContext.h" +#include "nsIPrincipal.h" +#include "nsIWebNavigation.h" +#include "nsNetUtil.h" +#include "nsIInputStream.h" +#include "nsLayoutUtils.h" +#include "nsPresContext.h" +#include "nsPrintfCString.h" +#include "nsUTF8Utils.h" +#include "nsDOMNavigationTiming.h" +#include "ReferrerInfo.h" + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +#define LOG(args) \ + MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug) + +NS_IMPL_ISUPPORTS0(FontFaceSetImpl) + +FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner) + : mMutex("mozilla::dom::FontFaceSetImpl"), + mOwner(aOwner), + mStatus(FontFaceSetLoadStatus::Loaded), + mNonRuleFacesDirty(false), + mHasLoadingFontFaces(false), + mHasLoadingFontFacesIsDirty(false), + mDelayedLoadCheck(false), + mBypassCache(false), + mPrivateBrowsing(false) {} + +FontFaceSetImpl::~FontFaceSetImpl() { + // Assert that we don't drop any FontFaceSet objects during a Servo traversal, + // since PostTraversalTask objects can hold raw pointers to FontFaceSets. + MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); + + Destroy(); +} + +void FontFaceSetImpl::DestroyLoaders() { + mMutex.AssertCurrentThreadIn(); + if (mLoaders.IsEmpty()) { + return; + } + if (NS_IsMainThread()) { + for (const auto& key : mLoaders.Keys()) { + key->Cancel(); + } + mLoaders.Clear(); + return; + } + + class DestroyLoadersRunnable final : public Runnable { + public: + explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet) + : Runnable("FontFaceSetImpl::DestroyLoaders"), + mFontFaceSet(aFontFaceSet) {} + + protected: + ~DestroyLoadersRunnable() override = default; + + NS_IMETHOD Run() override { + RecursiveMutexAutoLock lock(mFontFaceSet->mMutex); + mFontFaceSet->DestroyLoaders(); + return NS_OK; + } + + // We need to save a reference to the FontFaceSetImpl because the + // loaders contain a non-owning reference to it. + RefPtr mFontFaceSet; + }; + + auto runnable = MakeRefPtr(this); + NS_DispatchToMainThread(runnable); +} + +void FontFaceSetImpl::Destroy() { + nsTArray nonRuleFaces; + nsRefPtrHashtable fontFamilies; + + { + RecursiveMutexAutoLock lock(mMutex); + DestroyLoaders(); + nonRuleFaces = std::move(mNonRuleFaces); + fontFamilies = std::move(mFontFamilies); + mOwner = nullptr; + } + + if (gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList()) { + fp->RemoveUserFontSet(this); + } +} + +void FontFaceSetImpl::ParseFontShorthandForMatching( + const nsACString& aFont, StyleFontFamilyList& aFamilyList, + FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle, + ErrorResult& aRv) { + RefPtr url = GetURLExtraData(); + if (!url) { + aRv.ThrowInvalidStateError("Missing URLExtraData"); + return; + } + + if (!ServoCSSParser::ParseFontShorthandForMatching( + aFont, url, aFamilyList, aStyle, aStretch, aWeight)) { + aRv.ThrowSyntaxError("Invalid font shorthand"); + return; + } +} + +static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry, + const nsAString& aInput) { + const char16_t* p = aInput.Data(); + const char16_t* end = p + aInput.Length(); + + while (p < end) { + uint32_t c = UTF16CharEnumerator::NextChar(&p, end); + if (aEntry->CharacterInUnicodeRange(c)) { + return true; + } + } + return false; +} + +void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont, + const nsAString& aText, + nsTArray& aFontFaces, + ErrorResult& aRv) { + RecursiveMutexAutoLock lock(mMutex); + + StyleFontFamilyList familyList; + FontWeight weight; + FontStretch stretch; + FontSlantStyle italicStyle; + ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle, + aRv); + if (aRv.Failed()) { + return; + } + + gfxFontStyle style; + style.style = italicStyle; + style.weight = weight; + style.stretch = stretch; + + // Set of FontFaces that we want to return. + nsTHashSet matchingFaces; + + for (const StyleSingleFontFamily& fontFamilyName : familyList.list.AsSpan()) { + if (!fontFamilyName.IsFamilyName()) { + continue; + } + + const auto& name = fontFamilyName.AsFamilyName(); + RefPtr family = + LookupFamily(nsAtomCString(name.name.AsAtom())); + + if (!family) { + continue; + } + + AutoTArray entries; + family->FindAllFontsForStyle(style, entries); + + for (gfxFontEntry* e : entries) { + FontFaceImpl::Entry* entry = static_cast(e); + if (HasAnyCharacterInUnicodeRange(entry, aText)) { + entry->FindFontFaceOwners(matchingFaces); + } + } + } + + if (matchingFaces.IsEmpty()) { + return; + } + + // Add all FontFaces in matchingFaces to aFontFaces, in the order + // they appear in the FontFaceSet. + FindMatchingFontFaces(matchingFaces, aFontFaces); +} + +void FontFaceSetImpl::FindMatchingFontFaces( + const nsTHashSet& aMatchingFaces, + nsTArray& aFontFaces) { + RecursiveMutexAutoLock lock(mMutex); + for (FontFaceRecord& record : mNonRuleFaces) { + FontFace* owner = record.mFontFace->GetOwner(); + if (owner && aMatchingFaces.Contains(owner)) { + aFontFaces.AppendElement(owner); + } + } +} + +bool FontFaceSetImpl::ReadyPromiseIsPending() const { + RecursiveMutexAutoLock lock(mMutex); + return mOwner && mOwner->ReadyPromiseIsPending(); +} + +FontFaceSetLoadStatus FontFaceSetImpl::Status() { + RecursiveMutexAutoLock lock(mMutex); + FlushUserFontSet(); + return mStatus; +} + +bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) { + RecursiveMutexAutoLock lock(mMutex); + FlushUserFontSet(); + + if (aFontFace->IsInFontFaceSet(this)) { + return false; + } + + if (aFontFace->HasRule()) { + aRv.ThrowInvalidModificationError( + "Can't add face to FontFaceSet that comes from an @font-face rule"); + return false; + } + + aFontFace->AddFontFaceSet(this); + +#ifdef DEBUG + for (const FontFaceRecord& rec : mNonRuleFaces) { + MOZ_ASSERT(rec.mFontFace != aFontFace, + "FontFace should not occur in mNonRuleFaces twice"); + } +#endif + + FontFaceRecord* rec = mNonRuleFaces.AppendElement(); + rec->mFontFace = aFontFace; + rec->mOrigin = Nothing(); + + mNonRuleFacesDirty = true; + MarkUserFontSetDirty(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingStarted(); + return true; +} + +void FontFaceSetImpl::Clear() { + RecursiveMutexAutoLock lock(mMutex); + FlushUserFontSet(); + + if (mNonRuleFaces.IsEmpty()) { + return; + } + + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + FontFaceImpl* f = mNonRuleFaces[i].mFontFace; + f->RemoveFontFaceSet(this); + } + + mNonRuleFaces.Clear(); + mNonRuleFacesDirty = true; + MarkUserFontSetDirty(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingFinished(); +} + +bool FontFaceSetImpl::Delete(FontFaceImpl* aFontFace) { + RecursiveMutexAutoLock lock(mMutex); + FlushUserFontSet(); + + if (aFontFace->HasRule()) { + return false; + } + + bool removed = false; + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + if (mNonRuleFaces[i].mFontFace == aFontFace) { + mNonRuleFaces.RemoveElementAt(i); + removed = true; + break; + } + } + if (!removed) { + return false; + } + + aFontFace->RemoveFontFaceSet(this); + + mNonRuleFacesDirty = true; + MarkUserFontSetDirty(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingFinished(); + return true; +} + +bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl* aFontFace) { + return aFontFace->IsInFontFaceSet(this); +} + +void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) { + RecursiveMutexAutoLock lock(mMutex); + mLoaders.RemoveEntry(aLoader); +} + +void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace) { + gfxUserFontAttributes attr; + if (!aFontFace->GetAttributes(attr)) { + // If there is no family name, this rule cannot contribute a + // usable font, so there is no point in processing it further. + return; + } + + nsAutoCString family(attr.mFamilyName); + + // Just create a new font entry if we haven't got one already. + if (!aFontFace->GetUserFontEntry()) { + // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does? + RefPtr entry = FindOrCreateUserFontEntryFromFontFace( + aFontFace, std::move(attr), StyleOrigin::Author); + if (!entry) { + return; + } + aFontFace->SetUserFontEntry(entry); + } + AddUserFontEntry(family, aFontFace->GetUserFontEntry()); +} + +void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry* aEntry, + gfxUserFontAttributes&& aAttr) { + MOZ_ASSERT(NS_IsMainThread()); + + bool resetFamilyName = !aEntry->mFamilyName.IsEmpty() && + aEntry->mFamilyName != aAttr.mFamilyName; + // aFontFace already has a user font entry, so we update its attributes + // rather than creating a new one. + aEntry->UpdateAttributes(std::move(aAttr)); + // If the family name has changed, remove the entry from its current family + // and clear the mFamilyName field so it can be reset when added to a new + // family. + if (resetFamilyName) { + RefPtr family = LookupFamily(aEntry->mFamilyName); + if (family) { + family->RemoveFontEntry(aEntry); + } + aEntry->mFamilyName.Truncate(0); + } +} + +class FontFaceSetImpl::UpdateUserFontEntryRunnable final + : public WorkerMainThreadRunnable { + public: + UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry, + gfxUserFontAttributes& aAttr) + : WorkerMainThreadRunnable( + GetCurrentThreadWorkerPrivate(), + "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns), + mSet(aSet), + mEntry(aEntry), + mAttr(aAttr) {} + + bool MainThreadRun() override { + mSet->UpdateUserFontEntry(mEntry, std::move(mAttr)); + return true; + } + + private: + FontFaceSetImpl* mSet; + gfxUserFontEntry* mEntry; + gfxUserFontAttributes& mAttr; +}; + +// TODO(emilio): Should this take an nsAtom* aFamilyName instead? +// +// All callers have one handy. +/* static */ +already_AddRefed +FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace( + FontFaceImpl* aFontFace, gfxUserFontAttributes&& aAttr, + StyleOrigin aOrigin) { + FontFaceSetImpl* set = aFontFace->GetPrimaryFontFaceSet(); + + RefPtr existingEntry = aFontFace->GetUserFontEntry(); + if (existingEntry) { + if (NS_IsMainThread()) { + set->UpdateUserFontEntry(existingEntry, std::move(aAttr)); + } else { + auto task = + MakeRefPtr(set, existingEntry, aAttr); + IgnoredErrorResult ignoredRv; + task->Dispatch(Canceling, ignoredRv); + } + return existingEntry.forget(); + } + + // set up src array + nsTArray srcArray; + + if (aFontFace->HasFontData()) { + gfxFontFaceSrc* face = srcArray.AppendElement(); + if (!face) { + return nullptr; + } + + face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer; + face->mBuffer = aFontFace->TakeBufferSource(); + } else { + size_t len = aAttr.mSources.Length(); + for (size_t i = 0; i < len; ++i) { + gfxFontFaceSrc* face = srcArray.AppendElement(); + const auto& component = aAttr.mSources[i]; + switch (component.tag) { + case StyleFontFaceSourceListComponent::Tag::Local: { + nsAtom* atom = component.AsLocal(); + face->mLocalName.Append(nsAtomCString(atom)); + face->mSourceType = gfxFontFaceSrc::eSourceType_Local; + face->mURI = nullptr; + face->mFormatHint = StyleFontFaceSourceFormatKeyword::None; + break; + } + + case StyleFontFaceSourceListComponent::Tag::Url: { + face->mSourceType = gfxFontFaceSrc::eSourceType_URL; + const StyleCssUrl* url = component.AsUrl(); + nsIURI* uri = url->GetURI(); + face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr; + const URLExtraData& extraData = url->ExtraData(); + face->mReferrerInfo = extraData.ReferrerInfo(); + + // agent and user stylesheets are treated slightly differently, + // the same-site origin check and access control headers are + // enforced against the sheet principal rather than the document + // principal to allow user stylesheets to include @font-face rules + if (aOrigin == StyleOrigin::User || + aOrigin == StyleOrigin::UserAgent) { + face->mUseOriginPrincipal = true; + face->mOriginPrincipal = new gfxFontSrcPrincipal( + extraData.Principal(), extraData.Principal()); + } + + face->mLocalName.Truncate(); + face->mFormatHint = StyleFontFaceSourceFormatKeyword::None; + face->mTechFlags = StyleFontFaceSourceTechFlags::Empty(); + + if (i + 1 < len) { + // Check for a format hint. + const auto& next = aAttr.mSources[i + 1]; + switch (next.tag) { + case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword: + face->mFormatHint = next.format_hint_keyword._0; + i++; + break; + case StyleFontFaceSourceListComponent::Tag::FormatHintString: { + nsDependentCSubstring valueString( + reinterpret_cast( + next.format_hint_string.utf8_bytes), + next.format_hint_string.length); + + if (valueString.LowerCaseEqualsASCII("woff")) { + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff; + } else if (valueString.LowerCaseEqualsASCII("woff2")) { + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2; + } else if (valueString.LowerCaseEqualsASCII("opentype")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Opentype; + } else if (valueString.LowerCaseEqualsASCII("truetype")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Truetype; + } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Truetype; + } else if (valueString.LowerCaseEqualsASCII( + "embedded-opentype")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::EmbeddedOpentype; + } else if (valueString.LowerCaseEqualsASCII("svg")) { + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Svg; + } else if (StaticPrefs::layout_css_font_variations_enabled()) { + // Non-standard values that Firefox accepted, for back-compat; + // these are superseded by the tech() function. + if (valueString.LowerCaseEqualsASCII("woff-variations")) { + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff; + } else if (valueString.LowerCaseEqualsASCII( + "woff2-variations")) { + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2; + } else if (valueString.LowerCaseEqualsASCII( + "opentype-variations")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Opentype; + } else if (valueString.LowerCaseEqualsASCII( + "truetype-variations")) { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Truetype; + } else { + face->mFormatHint = + StyleFontFaceSourceFormatKeyword::Unknown; + } + } else { + // unknown format specified, mark to distinguish from the + // case where no format hints are specified + face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown; + } + i++; + break; + } + case StyleFontFaceSourceListComponent::Tag::TechFlags: + case StyleFontFaceSourceListComponent::Tag::Local: + case StyleFontFaceSourceListComponent::Tag::Url: + break; + } + } + + if (i + 1 < len) { + // Check for a set of font-technologies flags. + const auto& next = aAttr.mSources[i + 1]; + if (next.IsTechFlags()) { + face->mTechFlags = next.AsTechFlags(); + i++; + } + } + + if (!face->mURI) { + // if URI not valid, omit from src array + srcArray.RemoveLastElement(); + NS_WARNING("null url in @font-face rule"); + continue; + } + break; + } + + case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword: + case StyleFontFaceSourceListComponent::Tag::FormatHintString: + case StyleFontFaceSourceListComponent::Tag::TechFlags: + MOZ_ASSERT_UNREACHABLE( + "Should always come after a URL source, and be consumed already"); + break; + } + } + } + + if (srcArray.IsEmpty()) { + return nullptr; + } + + return set->FindOrCreateUserFontEntry(std::move(srcArray), std::move(aAttr)); +} + +nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry, + uint32_t aSrcIndex, const char* aMessage, + uint32_t aFlags, nsresult aStatus) { + nsAutoCString familyName; + nsAutoCString fontURI; + aUserFontEntry->GetFamilyNameAndURIForLogging(aSrcIndex, familyName, fontURI); + + nsAutoCString weightString; + aUserFontEntry->Weight().ToString(weightString); + nsAutoCString stretchString; + aUserFontEntry->Stretch().ToString(stretchString); + nsPrintfCString message( + "downloadable font: %s " + "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)", + aMessage, familyName.get(), + aUserFontEntry->IsItalic() ? "italic" : "normal", // XXX todo: oblique? + weightString.get(), stretchString.get(), aSrcIndex); + + if (NS_FAILED(aStatus)) { + message.AppendLiteral(": "); + switch (aStatus) { + case NS_ERROR_DOM_BAD_URI: + message.AppendLiteral("bad URI or cross-site access not allowed"); + break; + case NS_ERROR_CONTENT_BLOCKED: + message.AppendLiteral("content blocked"); + break; + default: + message.AppendLiteral("status="); + message.AppendInt(static_cast(aStatus)); + break; + } + } + message.AppendLiteral(" source: "); + message.Append(fontURI); + + LOG(("userfonts (%p) %s", this, message.get())); + + if (GetCurrentThreadWorkerPrivate()) { + // TODO(aosmond): Log to the console for workers. See bug 1778537. + return NS_OK; + } + + nsCOMPtr console( + do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (!console) { + return NS_ERROR_NOT_AVAILABLE; + } + + // try to give the user an indication of where the rule came from + StyleLockedFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry); + nsString href; + nsAutoCString text; + uint32_t line = 0; + uint32_t column = 0; + if (rule) { + Servo_FontFaceRule_GetCssText(rule, &text); + Servo_FontFaceRule_GetSourceLocation(rule, &line, &column); + // FIXME We need to figure out an approach to get the style sheet + // of this raw rule. See bug 1450903. +#if 0 + StyleSheet* sheet = rule->GetStyleSheet(); + // if the style sheet is removed while the font is loading can be null + if (sheet) { + nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault(); + CopyUTF8toUTF16(spec, href); + } else { + NS_WARNING("null parent stylesheet for @font-face rule"); + href.AssignLiteral("unknown"); + } +#endif + // Leave href empty if we don't know how to get the correct sheet. + } + + nsresult rv; + nsCOMPtr scriptError = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message), + href, // file + NS_ConvertUTF8toUTF16(text), // src line + line, column, + aFlags, // flags + "CSS Loader", // category (make separate?) + GetInnerWindowID()); + if (NS_SUCCEEDED(rv)) { + console->LogMessage(scriptError); + } + + return NS_OK; +} + +nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength) { + nsCOMPtr channel; + nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel), + aFontToLoad, aFontFaceSrc); + NS_ENSURE_SUCCESS(rv, rv); + + // blocking stream is OK for data URIs + nsCOMPtr stream; + rv = channel->Open(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t bufferLength64; + rv = stream->Available(&bufferLength64); + NS_ENSURE_SUCCESS(rv, rv); + if (bufferLength64 == 0) { + return NS_ERROR_FAILURE; + } + if (bufferLength64 > UINT32_MAX) { + return NS_ERROR_FILE_TOO_BIG; + } + aBufferLength = static_cast(bufferLength64); + + // read all the decoded data + aBuffer = static_cast(malloc(sizeof(uint8_t) * aBufferLength)); + if (!aBuffer) { + aBufferLength = 0; + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t numRead, totalRead = 0; + while (NS_SUCCEEDED( + rv = stream->Read(reinterpret_cast(aBuffer + totalRead), + aBufferLength - totalRead, &numRead)) && + numRead != 0) { + totalRead += numRead; + if (totalRead > aBufferLength) { + rv = NS_ERROR_FAILURE; + break; + } + } + + // make sure there's a mime type + if (NS_SUCCEEDED(rv)) { + nsAutoCString mimeType; + rv = channel->GetContentType(mimeType); + aBufferLength = totalRead; + } + + if (NS_FAILED(rv)) { + free(aBuffer); + aBuffer = nullptr; + aBufferLength = 0; + return rv; + } + + return NS_OK; +} + +void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl* aFontFace) { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + RecursiveMutexAutoLock lock(mMutex); + MOZ_ASSERT(HasAvailableFontFace(aFontFace)); + + mHasLoadingFontFacesIsDirty = true; + + if (aFontFace->Status() == FontFaceLoadStatus::Loading) { + CheckLoadingStarted(); + } else { + MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded || + aFontFace->Status() == FontFaceLoadStatus::Error); + // When a font finishes downloading, nsPresContext::UserFontSetUpdated + // will be called immediately afterwards to request a reflow of the + // relevant elements in the document. We want to wait until the reflow + // request has been done before the FontFaceSet is marked as Loaded so + // that we don't briefly set the FontFaceSet to Loaded and then Loading + // again once the reflow is pending. So we go around the event loop + // and call CheckLoadingFinished() after the reflow has been queued. + if (!mDelayedLoadCheck) { + mDelayedLoadCheck = true; + DispatchCheckLoadingFinishedAfterDelay(); + } + } +} + +void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + + if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { + // See comments in Gecko_GetFontMetrics. + // + // We can't just dispatch the runnable below if we're not on the main + // thread, since it needs to take a strong reference to the FontFaceSet, + // and being a DOM object, FontFaceSet doesn't support thread-safe + // refcounting. + set->AppendTask( + PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay( + this)); + return; + } + + DispatchToOwningThread( + "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay", + [self = RefPtr{this}]() { self->CheckLoadingFinishedAfterDelay(); }); +} + +void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() { + RecursiveMutexAutoLock lock(mMutex); + mDelayedLoadCheck = false; + CheckLoadingFinished(); +} + +void FontFaceSetImpl::CheckLoadingStarted() { + gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); + RecursiveMutexAutoLock lock(mMutex); + + if (!HasLoadingFontFaces()) { + return; + } + + if (mStatus == FontFaceSetLoadStatus::Loading) { + // We have already dispatched a loading event and replaced mReady + // with a fresh, unresolved promise. + return; + } + + mStatus = FontFaceSetLoadStatus::Loading; + + if (IsOnOwningThread()) { + OnLoadingStarted(); + return; + } + + DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted", + [self = RefPtr{this}]() { self->OnLoadingStarted(); }); +} + +void FontFaceSetImpl::OnLoadingStarted() { + RecursiveMutexAutoLock lock(mMutex); + if (mOwner) { + mOwner->DispatchLoadingEventAndReplaceReadyPromise(); + } +} + +void FontFaceSetImpl::UpdateHasLoadingFontFaces() { + RecursiveMutexAutoLock lock(mMutex); + mHasLoadingFontFacesIsDirty = false; + mHasLoadingFontFaces = false; + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) { + mHasLoadingFontFaces = true; + return; + } + } +} + +bool FontFaceSetImpl::HasLoadingFontFaces() { + RecursiveMutexAutoLock lock(mMutex); + if (mHasLoadingFontFacesIsDirty) { + UpdateHasLoadingFontFaces(); + } + return mHasLoadingFontFaces; +} + +bool FontFaceSetImpl::MightHavePendingFontLoads() { + // Check for FontFace objects in the FontFaceSet that are still loading. + return HasLoadingFontFaces(); +} + +void FontFaceSetImpl::CheckLoadingFinished() { + RecursiveMutexAutoLock lock(mMutex); + if (mDelayedLoadCheck) { + // Wait until the runnable posted in OnFontFaceStatusChanged calls us. + return; + } + + if (!ReadyPromiseIsPending()) { + // We've already resolved mReady (or set the flag to do that lazily) and + // dispatched the loadingdone/loadingerror events. + return; + } + + if (MightHavePendingFontLoads()) { + // We're not finished loading yet. + return; + } + + mStatus = FontFaceSetLoadStatus::Loaded; + + if (IsOnOwningThread()) { + OnLoadingFinished(); + return; + } + + DispatchToOwningThread( + "FontFaceSetImpl::CheckLoadingFinished", + [self = RefPtr{this}]() { self->OnLoadingFinished(); }); +} + +void FontFaceSetImpl::OnLoadingFinished() { + RecursiveMutexAutoLock lock(mMutex); + if (mOwner) { + mOwner->MaybeResolve(); + } +} + +void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() { + RecursiveMutexAutoLock lock(mMutex); + mAllowedFontLoads.Clear(); + IncrementGeneration(false); +} + +// -- gfxUserFontSet +// ------------------------------------------------ + +already_AddRefed +FontFaceSetImpl::GetStandardFontLoadPrincipal() const { + RecursiveMutexAutoLock lock(mMutex); + return RefPtr{mStandardFontLoadPrincipal}.forget(); +} + +void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize, + TimeStamp aDoneTime) { + mDownloadCount++; + mDownloadSize += aFontSize; + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024); + + TimeStamp navStart = GetNavigationStartTimeStamp(); + TimeStamp zero; + if (navStart != zero) { + mozilla::glean::network::font_download_end.AccumulateRawDuration(aDoneTime - + navStart); + } +} + +void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); } + +already_AddRefed FontFaceSetImpl::CreateUserFontEntry( + nsTArray&& aFontFaceSrcList, + gfxUserFontAttributes&& aAttr) { + RefPtr entry = new FontFaceImpl::Entry( + this, std::move(aFontFaceSrcList), std::move(aAttr)); + return entry.forget(); +} + +void FontFaceSetImpl::ForgetLocalFaces() { + // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so + // we need to make a copy of the table first. + nsTArray> fontFamilies; + { + RecursiveMutexAutoLock lock(mMutex); + fontFamilies.SetCapacity(mFontFamilies.Count()); + for (const auto& fam : mFontFamilies.Values()) { + fontFamilies.AppendElement(fam); + } + } + + for (const auto& fam : fontFamilies) { + ForgetLocalFace(fam); + } +} + +already_AddRefed FontFaceSetImpl::GetFamily( + const nsACString& aFamilyName) { + RecursiveMutexAutoLock lock(mMutex); + return gfxUserFontSet::GetFamily(aFamilyName); +} + +#undef LOG_ENABLED +#undef LOG diff --git a/layout/style/FontFaceSetImpl.h b/layout/style/FontFaceSetImpl.h new file mode 100644 index 0000000000..6f245c6599 --- /dev/null +++ b/layout/style/FontFaceSetImpl.h @@ -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 http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_FontFaceSetImpl_h +#define mozilla_dom_FontFaceSetImpl_h + +#include "mozilla/dom/FontFace.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/RecursiveMutex.h" +#include "gfxUserFontSet.h" +#include "nsICSSLoaderObserver.h" +#include "nsIDOMEventListener.h" + +#include + +struct gfxFontFaceSrc; +class gfxFontSrcPrincipal; +class gfxUserFontEntry; +class nsFontFaceLoader; +class nsIChannel; +class nsIPrincipal; +class nsPIDOMWindowInner; + +namespace mozilla { +struct StyleLockedFontFaceRule; +class PostTraversalTask; +class Runnable; +class SharedFontList; +namespace dom { +class FontFace; +} // namespace dom +} // namespace mozilla + +namespace mozilla::dom { + +class FontFaceSetImpl : public nsISupports, public gfxUserFontSet { + NS_DECL_THREADSAFE_ISUPPORTS + + public: + // gfxUserFontSet + + already_AddRefed GetStandardFontLoadPrincipal() + const final; + + void RecordFontLoadDone(uint32_t aFontSize, TimeStamp aDoneTime) override; + + bool BypassCache() final { return mBypassCache; } + + void ForgetLocalFaces() final; + + protected: + virtual nsresult CreateChannelForSyncLoadFontData( + nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc) = 0; + + // gfxUserFontSet + + bool GetPrivateBrowsing() override { return mPrivateBrowsing; } + nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength) override; + nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, uint32_t aSrcIndex, + const char* aMessage, + uint32_t aFlags = nsIScriptError::errorFlag, + nsresult aStatus = NS_OK) override; + void DoRebuildUserFontSet() override; + already_AddRefed CreateUserFontEntry( + nsTArray&& aFontFaceSrcList, + gfxUserFontAttributes&& aAttr) override; + + already_AddRefed GetFamily( + const nsACString& aFamilyName) final; + + explicit FontFaceSetImpl(FontFaceSet* aOwner); + + void DestroyLoaders(); + + public: + virtual void Destroy(); + virtual bool IsOnOwningThread() = 0; +#ifdef DEBUG + virtual void AssertIsOnOwningThread() = 0; +#else + void AssertIsOnOwningThread() {} +#endif + virtual void DispatchToOwningThread(const char* aName, + std::function&& aFunc) = 0; + + // Called by nsFontFaceLoader when the loader has completed normally. + // It's removed from the mLoaders set. + virtual void RemoveLoader(nsFontFaceLoader* aLoader); + + virtual bool UpdateRules(const nsTArray& aRules) { + MOZ_ASSERT_UNREACHABLE("Not implemented!"); + return false; + } + + // search for @font-face rule that matches a platform font entry + virtual StyleLockedFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry) { + MOZ_ASSERT_UNREACHABLE("Not implemented!"); + return nullptr; + } + + /** + * Finds an existing entry in the user font cache or creates a new user + * font entry for the given FontFace object. + */ + static already_AddRefed + FindOrCreateUserFontEntryFromFontFace(FontFaceImpl* aFontFace, + gfxUserFontAttributes&& aAttr, + StyleOrigin); + + /** + * Notification method called by a FontFace to indicate that its loading + * status has changed. + */ + virtual void OnFontFaceStatusChanged(FontFaceImpl* aFontFace); + + /** + * Notification method called by the nsPresContext to indicate that the + * refresh driver ticked and flushed style and layout. + * were just flushed. + */ + virtual void DidRefresh() { MOZ_ASSERT_UNREACHABLE("Not implemented!"); } + + virtual void FlushUserFontSet() = 0; + + static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) { + const auto* set = static_cast(aUserFontSet); + return set ? set->GetPresContext() : nullptr; + } + + virtual void RefreshStandardFontLoadPrincipal(); + + virtual dom::Document* GetDocument() const { return nullptr; } + + virtual already_AddRefed GetURLExtraData() = 0; + + // -- Web IDL -------------------------------------------------------------- + + virtual void EnsureReady() {} + dom::FontFaceSetLoadStatus Status(); + + virtual bool Add(FontFaceImpl* aFontFace, ErrorResult& aRv); + virtual void Clear(); + virtual bool Delete(FontFaceImpl* aFontFace); + + // For ServoStyleSet to know ahead of time whether a font is loadable. + virtual void CacheFontLoadability() { + MOZ_ASSERT_UNREACHABLE("Not implemented!"); + } + + virtual void MarkUserFontSetDirty() {} + + /** + * Checks to see whether it is time to resolve mReady and dispatch any + * "loadingdone" and "loadingerror" events. + */ + virtual void CheckLoadingFinished(); + + virtual void FindMatchingFontFaces(const nsACString& aFont, + const nsAString& aText, + nsTArray& aFontFaces, + ErrorResult& aRv); + + virtual void DispatchCheckLoadingFinishedAfterDelay(); + + protected: + ~FontFaceSetImpl() override; + + virtual uint64_t GetInnerWindowID() = 0; + + /** + * Returns whether the given FontFace is currently "in" the FontFaceSet. + */ + bool HasAvailableFontFace(FontFaceImpl* aFontFace); + + /** + * Returns whether there might be any pending font loads, which should cause + * the mReady Promise not to be resolved yet. + */ + virtual bool MightHavePendingFontLoads(); + + /** + * Checks to see whether it is time to replace mReady and dispatch a + * "loading" event. + */ + void CheckLoadingStarted(); + + /** + * Callback for invoking CheckLoadingFinished after going through the + * event loop. See OnFontFaceStatusChanged. + */ + void CheckLoadingFinishedAfterDelay(); + + void OnLoadingStarted(); + void OnLoadingFinished(); + + // Note: if you add new cycle collected objects to FontFaceRecord, + // make sure to update FontFaceSet's cycle collection macros + // accordingly. + struct FontFaceRecord { + RefPtr mFontFace; + Maybe mOrigin; // only relevant for mRuleFaces entries + }; + + // search for @font-face rule that matches a userfont font entry + virtual StyleLockedFontFaceRule* FindRuleForUserFontEntry( + gfxUserFontEntry* aUserFontEntry) { + return nullptr; + } + + virtual void FindMatchingFontFaces( + const nsTHashSet& aMatchingFaces, + nsTArray& aFontFaces); + + class UpdateUserFontEntryRunnable; + void UpdateUserFontEntry(gfxUserFontEntry* aEntry, + gfxUserFontAttributes&& aAttr); + + nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + gfxFontSrcPrincipal** aPrincipal, bool* aBypassCache); + + void InsertNonRuleFontFace(FontFaceImpl* aFontFace); + + /** + * Returns whether we have any loading FontFace objects in the FontFaceSet. + */ + bool HasLoadingFontFaces(); + + // Whether mReady is pending, or would be when created. + bool ReadyPromiseIsPending() const; + + // Helper function for HasLoadingFontFaces. + virtual void UpdateHasLoadingFontFaces(); + + void ParseFontShorthandForMatching(const nsACString& aFont, + StyleFontFamilyList& aFamilyList, + FontWeight& aWeight, FontStretch& aStretch, + FontSlantStyle& aStyle, ErrorResult& aRv); + + virtual TimeStamp GetNavigationStartTimeStamp() = 0; + + mutable RecursiveMutex mMutex; + + FontFaceSet* MOZ_NON_OWNING_REF mOwner MOZ_GUARDED_BY(mMutex); + + // The document's node principal, which is the principal font loads for + // this FontFaceSet will generally use. (This principal is not used for + // @font-face rules in UA and user sheets, where the principal of the + // sheet is used instead.) + // + // This field is used from GetStandardFontLoadPrincipal. When on a + // style worker thread, we use mStandardFontLoadPrincipal assuming + // it is up to date. + // + // Because mDocument's principal can change over time, + // its value must be updated by a call to ResetStandardFontLoadPrincipal. + mutable RefPtr mStandardFontLoadPrincipal + MOZ_GUARDED_BY(mMutex); + + // Set of all loaders pointing to us. These are not strong pointers, + // but that's OK because nsFontFaceLoader always calls RemoveLoader on + // us before it dies (unless we die first). + nsTHashtable> mLoaders MOZ_GUARDED_BY(mMutex); + + // The non rule backed FontFace objects that have been added to this + // FontFaceSet. + nsTArray mNonRuleFaces MOZ_GUARDED_BY(mMutex); + + // The overall status of the loading or loaded fonts in the FontFaceSet. + dom::FontFaceSetLoadStatus mStatus MOZ_GUARDED_BY(mMutex); + + // A map from gfxFontFaceSrc pointer identity to whether the load is allowed + // by CSP or other checks. We store this here because querying CSP off the + // main thread is not a great idea. + // + // We could use just the pointer and use this as a hash set, but then we'd + // have no way to verify that we've checked all the loads we should. + nsTHashMap, bool> mAllowedFontLoads + MOZ_GUARDED_BY(mMutex); + + // Whether mNonRuleFaces has changed since last time UpdateRules ran. + bool mNonRuleFacesDirty MOZ_GUARDED_BY(mMutex); + + // Whether any FontFace objects in mRuleFaces or mNonRuleFaces are + // loading. Only valid when mHasLoadingFontFacesIsDirty is false. Don't use + // this variable directly; call the HasLoadingFontFaces method instead. + bool mHasLoadingFontFaces MOZ_GUARDED_BY(mMutex); + + // This variable is only valid when mLoadingDirty is false. + bool mHasLoadingFontFacesIsDirty MOZ_GUARDED_BY(mMutex); + + // Whether CheckLoadingFinished calls should be ignored. See comment in + // OnFontFaceStatusChanged. + bool mDelayedLoadCheck MOZ_GUARDED_BY(mMutex); + + // Whether the docshell for our document indicated that loads should + // bypass the cache. + bool mBypassCache; + + // Whether the docshell for our document indicates that we are in private + // browsing mode. + bool mPrivateBrowsing; +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFaceSetImpl_h) diff --git a/layout/style/FontFaceSetIterator.cpp b/layout/style/FontFaceSetIterator.cpp new file mode 100644 index 0000000000..ca05ee87d7 --- /dev/null +++ b/layout/style/FontFaceSetIterator.cpp @@ -0,0 +1,82 @@ +/* -*- 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 "mozilla/dom/FontFaceSetIterator.h" + +#include "js/Array.h" // JS::NewArrayObject + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION(FontFaceSetIterator, mFontFaceSet) + +FontFaceSetIterator::FontFaceSetIterator(FontFaceSet* aFontFaceSet, + bool aIsKeyAndValue) + : mFontFaceSet(aFontFaceSet), + mNextIndex(0), + mIsKeyAndValue(aIsKeyAndValue) { + MOZ_COUNT_CTOR(FontFaceSetIterator); +} + +FontFaceSetIterator::~FontFaceSetIterator() { + MOZ_COUNT_DTOR(FontFaceSetIterator); +} + +bool FontFaceSetIterator::WrapObject(JSContext* aCx, + JS::Handle aGivenProto, + JS::MutableHandle aReflector) { + return FontFaceSetIterator_Binding::Wrap(aCx, this, aGivenProto, aReflector); +} + +void FontFaceSetIterator::Next(JSContext* aCx, + FontFaceSetIteratorResult& aResult, + ErrorResult& aRv) { + if (!mFontFaceSet) { + aResult.mDone = true; + return; + } + + // Skip over non-Author origin fonts (GetFontFaceAt returns nullptr + // for those). + FontFace* face; + while (!(face = mFontFaceSet->GetFontFaceAt(mNextIndex++))) { + if (mNextIndex >= mFontFaceSet->SizeIncludingNonAuthorOrigins()) { + break; // this iterator is done + } + } + + if (!face) { + aResult.mValue.setUndefined(); + aResult.mDone = true; + mFontFaceSet = nullptr; + return; + } + + JS::Rooted value(aCx); + if (!ToJSValue(aCx, face, &value)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + if (mIsKeyAndValue) { + JS::RootedValueArray<2> values(aCx); + values[0].set(value); + values[1].set(value); + + JS::Rooted array(aCx); + array = JS::NewArrayObject(aCx, values); + if (array) { + aResult.mValue.setObject(*array); + } + } else { + aResult.mValue = value; + } + + aResult.mDone = false; +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/FontFaceSetIterator.h b/layout/style/FontFaceSetIterator.h new file mode 100644 index 0000000000..e18669e9b5 --- /dev/null +++ b/layout/style/FontFaceSetIterator.h @@ -0,0 +1,42 @@ +/* -*- 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_dom_FontFaceSetIterator_h +#define mozilla_dom_FontFaceSetIterator_h + +#include "mozilla/dom/FontFaceSet.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/dom/NonRefcountedDOMObject.h" + +namespace mozilla { +namespace dom { + +class FontFaceSetIterator final { + public: + FontFaceSetIterator(FontFaceSet*, bool aIsKeyAndValue); + + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(FontFaceSetIterator) + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(FontFaceSetIterator) + + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle aReflector); + + // WebIDL + void Next(JSContext* aCx, FontFaceSetIteratorResult& aResult, + ErrorResult& aRv); + + private: + ~FontFaceSetIterator(); + + RefPtr mFontFaceSet; + uint32_t mNextIndex; + bool mIsKeyAndValue; +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_FontFaceSetIterator_h) diff --git a/layout/style/FontFaceSetWorkerImpl.cpp b/layout/style/FontFaceSetWorkerImpl.cpp new file mode 100644 index 0000000000..015c9e5b18 --- /dev/null +++ b/layout/style/FontFaceSetWorkerImpl.cpp @@ -0,0 +1,374 @@ +/* -*- 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 "FontFaceSetWorkerImpl.h" +#include "mozilla/FontLoaderUtils.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/WorkerRunnable.h" +#include "mozilla/LoadInfo.h" +#include "nsContentPolicyUtils.h" +#include "nsFontFaceLoader.h" +#include "nsINetworkPredictor.h" +#include "nsIWebNavigation.h" + +using namespace mozilla; +using namespace mozilla::css; + +namespace mozilla::dom { + +#define LOG(...) \ + MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, \ + (__VA_ARGS__)) +#define LOG_ENABLED() \ + MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug) + +NS_IMPL_ISUPPORTS_INHERITED0(FontFaceSetWorkerImpl, FontFaceSetImpl); + +FontFaceSetWorkerImpl::FontFaceSetWorkerImpl(FontFaceSet* aOwner) + : FontFaceSetImpl(aOwner) {} + +FontFaceSetWorkerImpl::~FontFaceSetWorkerImpl() = default; + +bool FontFaceSetWorkerImpl::Initialize(WorkerPrivate* aWorkerPrivate) { + MOZ_ASSERT(aWorkerPrivate); + + RefPtr workerRef = + StrongWorkerRef::Create(aWorkerPrivate, "FontFaceSetWorkerImpl", + [self = RefPtr{this}] { self->Destroy(); }); + if (NS_WARN_IF(!workerRef)) { + return false; + } + + { + RecursiveMutexAutoLock lock(mMutex); + mWorkerRef = new ThreadSafeWorkerRef(workerRef); + } + + class InitRunnable final : public WorkerMainThreadRunnable { + public: + InitRunnable(WorkerPrivate* aWorkerPrivate, FontFaceSetWorkerImpl* aImpl) + : WorkerMainThreadRunnable(aWorkerPrivate, + "FontFaceSetWorkerImpl :: Initialize"_ns), + mImpl(aImpl) {} + + protected: + ~InitRunnable() override = default; + + bool MainThreadRun() override { + mImpl->InitializeOnMainThread(); + return true; + } + + FontFaceSetWorkerImpl* mImpl; + }; + + IgnoredErrorResult rv; + auto runnable = MakeRefPtr(aWorkerPrivate, this); + runnable->Dispatch(Canceling, rv); + return !NS_WARN_IF(rv.Failed()); +} + +void FontFaceSetWorkerImpl::InitializeOnMainThread() { + MOZ_ASSERT(NS_IsMainThread()); + RecursiveMutexAutoLock lock(mMutex); + + if (!mWorkerRef) { + return; + } + + WorkerPrivate* workerPrivate = mWorkerRef->Private(); + nsIPrincipal* principal = workerPrivate->GetPrincipal(); + nsIPrincipal* loadingPrincipal = workerPrivate->GetLoadingPrincipal(); + nsIPrincipal* partitionedPrincipal = workerPrivate->GetPartitionedPrincipal(); + nsIPrincipal* defaultPrincipal = principal ? principal : loadingPrincipal; + + nsLoadFlags loadFlags = workerPrivate->GetLoadFlags(); + uint32_t loadType = 0; + + // Get the top-level worker. + WorkerPrivate* topWorkerPrivate = workerPrivate; + WorkerPrivate* parent = workerPrivate->GetParent(); + while (parent) { + topWorkerPrivate = parent; + parent = topWorkerPrivate->GetParent(); + } + + // If the top-level worker is a dedicated worker and has a window, and the + // window has a docshell, the caching behavior of this worker should match + // that of that docshell. This matches the behaviour from + // WorkerScriptLoader::LoadScript. + if (topWorkerPrivate->IsDedicatedWorker()) { + nsCOMPtr window = topWorkerPrivate->GetWindow(); + if (window) { + nsCOMPtr docShell = window->GetDocShell(); + if (docShell) { + docShell->GetDefaultLoadFlags(&loadFlags); + docShell->GetLoadType(&loadType); + } + } + } + + // Record the state of the "bypass cache" flags now. In theory the load type + // of a docshell could change after the document is loaded, but handling that + // doesn't seem too important. This matches the behaviour from + // FontFaceSetDocumentImpl::Initialize. + mBypassCache = + ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) || + (loadFlags & nsIRequest::LOAD_BYPASS_CACHE); + + // Same for the "private browsing" flag. + if (defaultPrincipal) { + mPrivateBrowsing = defaultPrincipal->GetPrivateBrowsingId() > 0; + } + + mStandardFontLoadPrincipal = + MakeRefPtr(defaultPrincipal, partitionedPrincipal); + + mURLExtraData = + new URLExtraData(workerPrivate->GetBaseURI(), + workerPrivate->GetReferrerInfo(), defaultPrincipal); +} + +void FontFaceSetWorkerImpl::Destroy() { + RecursiveMutexAutoLock lock(mMutex); + + mWorkerRef = nullptr; + FontFaceSetImpl::Destroy(); +} + +bool FontFaceSetWorkerImpl::IsOnOwningThread() { + RecursiveMutexAutoLock lock(mMutex); + if (!mWorkerRef) { + return false; + } + + return mWorkerRef->Private()->IsOnCurrentThread(); +} + +#ifdef DEBUG +void FontFaceSetWorkerImpl::AssertIsOnOwningThread() { + RecursiveMutexAutoLock lock(mMutex); + if (mWorkerRef) { + MOZ_ASSERT(mWorkerRef->Private()->IsOnCurrentThread()); + } else { + // Asserting during cycle collection if we are tearing down the worker is + // difficult. The only other thread that uses FontFace(Set)Impl objects is + // the main thread (if created from a worker). + MOZ_ASSERT(!NS_IsMainThread()); + } +} +#endif + +void FontFaceSetWorkerImpl::DispatchToOwningThread( + const char* aName, std::function&& aFunc) { + RecursiveMutexAutoLock lock(mMutex); + if (!mWorkerRef) { + return; + } + + WorkerPrivate* workerPrivate = mWorkerRef->Private(); + if (workerPrivate->IsOnCurrentThread()) { + NS_DispatchToCurrentThread( + NS_NewCancelableRunnableFunction(aName, std::move(aFunc))); + return; + } + + class FontFaceSetWorkerRunnable final : public WorkerRunnable { + public: + FontFaceSetWorkerRunnable(WorkerPrivate* aWorkerPrivate, + std::function&& aFunc) + : WorkerRunnable(aWorkerPrivate, "FontFaceSetWorkerRunnable"), + mFunc(std::move(aFunc)) {} + + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { + mFunc(); + return true; + } + + private: + std::function mFunc; + }; + + RefPtr runnable = + new FontFaceSetWorkerRunnable(workerPrivate, std::move(aFunc)); + runnable->Dispatch(); +} + +uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() { + RecursiveMutexAutoLock lock(mMutex); + if (!mWorkerRef) { + return 0; + } + + return mWorkerRef->Private()->WindowID(); +} + +void FontFaceSetWorkerImpl::FlushUserFontSet() { + RecursiveMutexAutoLock lock(mMutex); + + // If there was a change to the mNonRuleFaces array, then there could + // have been a modification to the user font set. + const bool modified = mNonRuleFacesDirty; + mNonRuleFacesDirty = false; + + for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) { + InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace); + } + + // Remove any residual families that have no font entries. + for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) { + if (!it.Data()->FontListLength()) { + it.Remove(); + } + } + + if (modified) { + IncrementGeneration(true); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingStarted(); + CheckLoadingFinished(); + } +} + +already_AddRefed FontFaceSetWorkerImpl::LookupFamily( + const nsACString& aName) const { + RecursiveMutexAutoLock lock(mMutex); + return gfxUserFontSet::LookupFamily(aName); +} + +nsresult FontFaceSetWorkerImpl::StartLoad(gfxUserFontEntry* aUserFontEntry, + uint32_t aSrcIndex) { + RecursiveMutexAutoLock lock(mMutex); + + if (NS_WARN_IF(!mWorkerRef)) { + return NS_ERROR_FAILURE; + } + + nsresult rv; + + nsCOMPtr streamLoader; + + const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex); + + nsCOMPtr loadGroup(mWorkerRef->Private()->GetLoadGroup()); + nsCOMPtr channel; + rv = FontLoaderUtils::BuildChannel( + getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS, + dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src, + mWorkerRef->Private(), loadGroup, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr fontLoader = + new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel); + + if (LOG_ENABLED()) { + nsCOMPtr referrer = + src.mReferrerInfo ? src.mReferrerInfo->GetOriginalReferrer() : nullptr; + LOG("userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n", + fontLoader.get(), src.mURI->GetSpecOrDefault().get(), + referrer ? referrer->GetSpecOrDefault().get() : ""); + } + + rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, fontLoader); + NS_ENSURE_SUCCESS(rv, rv); + + rv = channel->AsyncOpen(streamLoader); + if (NS_FAILED(rv)) { + fontLoader->DropChannel(); // explicitly need to break ref cycle + } + + mLoaders.PutEntry(fontLoader); + + net::PredictorLearn(src.mURI->get(), mWorkerRef->Private()->GetBaseURI(), + nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup); + + if (NS_SUCCEEDED(rv)) { + fontLoader->StartedLoading(streamLoader); + // let the font entry remember the loader, in case we need to cancel it + aUserFontEntry->SetLoader(fontLoader); + } + + return rv; +} + +bool FontFaceSetWorkerImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { + MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL); + MOZ_ASSERT(NS_IsMainThread()); + + RecursiveMutexAutoLock lock(mMutex); + + if (aSrc.mUseOriginPrincipal) { + return true; + } + + if (NS_WARN_IF(!mWorkerRef)) { + return false; + } + + RefPtr gfxPrincipal = + aSrc.mURI->InheritsSecurityContext() ? nullptr + : aSrc.LoadPrincipal(*this); + + nsIPrincipal* principal = + gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr; + + nsCOMPtr secCheckLoadInfo = new net::LoadInfo( + mWorkerRef->Private()->GetLoadingPrincipal(), // loading principal + principal, // triggering principal + nullptr, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, + nsIContentPolicy::TYPE_FONT); + + int16_t shouldLoad = nsIContentPolicy::ACCEPT; + nsresult rv = + NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad, + nsContentUtils::GetContentPolicy()); + + return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); +} + +nsresult FontFaceSetWorkerImpl::CreateChannelForSyncLoadFontData( + nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc) { + RecursiveMutexAutoLock lock(mMutex); + if (NS_WARN_IF(!mWorkerRef)) { + return NS_ERROR_FAILURE; + } + + gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal(); + + // We only get here for data: loads, so it doesn't really matter whether we + // use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be more + // restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT. + return NS_NewChannelWithTriggeringPrincipal( + aOutChannel, aFontFaceSrc->mURI->get(), + mWorkerRef->Private()->GetLoadingPrincipal(), + principal ? principal->NodePrincipal() : nullptr, + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, + aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT + : nsIContentPolicy::TYPE_FONT); +} + +nsPresContext* FontFaceSetWorkerImpl::GetPresContext() const { return nullptr; } + +TimeStamp FontFaceSetWorkerImpl::GetNavigationStartTimeStamp() { + RecursiveMutexAutoLock lock(mMutex); + if (!mWorkerRef) { + return TimeStamp(); + } + + return mWorkerRef->Private()->CreationTimeStamp(); +} + +already_AddRefed FontFaceSetWorkerImpl::GetURLExtraData() { + RecursiveMutexAutoLock lock(mMutex); + return RefPtr{mURLExtraData}.forget(); +} + +#undef LOG_ENABLED +#undef LOG + +} // namespace mozilla::dom diff --git a/layout/style/FontFaceSetWorkerImpl.h b/layout/style/FontFaceSetWorkerImpl.h new file mode 100644 index 0000000000..b699f8d52d --- /dev/null +++ b/layout/style/FontFaceSetWorkerImpl.h @@ -0,0 +1,68 @@ +/* -*- 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_dom_FontFaceSetWorkerImpl_h +#define mozilla_dom_FontFaceSetWorkerImpl_h + +#include "mozilla/dom/FontFaceSetImpl.h" + +namespace mozilla::dom { +class ThreadSafeWorkerRef; +class WorkerPrivate; + +class FontFaceSetWorkerImpl final : public FontFaceSetImpl { + NS_DECL_ISUPPORTS_INHERITED + + public: + explicit FontFaceSetWorkerImpl(FontFaceSet* aOwner); + + bool Initialize(WorkerPrivate* aWorkerPrivate); + void Destroy() override; + + bool IsOnOwningThread() override; +#ifdef DEBUG + void AssertIsOnOwningThread() override; +#endif + void DispatchToOwningThread(const char* aName, + std::function&& aFunc) override; + + already_AddRefed GetURLExtraData() override; + + void FlushUserFontSet() override; + + // gfxUserFontSet + + already_AddRefed LookupFamily( + const nsACString& aName) const override; + + nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, + uint32_t aSrcIndex) override; + + bool IsFontLoadAllowed(const gfxFontFaceSrc&) override; + + nsPresContext* GetPresContext() const override; + + private: + ~FontFaceSetWorkerImpl() override; + + void InitializeOnMainThread(); + + uint64_t GetInnerWindowID() override; + + nsresult CreateChannelForSyncLoadFontData( + nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc) override; + + TimeStamp GetNavigationStartTimeStamp() override; + + RefPtr mWorkerRef MOZ_GUARDED_BY(mMutex); + + RefPtr mURLExtraData MOZ_GUARDED_BY(mMutex); +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_FontFaceSetWorkerImpl_h) diff --git a/layout/style/FontLoaderUtils.cpp b/layout/style/FontLoaderUtils.cpp new file mode 100644 index 0000000000..9690e703fd --- /dev/null +++ b/layout/style/FontLoaderUtils.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 "mozilla/FontLoaderUtils.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/ReferrerInfo.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "gfxUserFontSet.h" +#include "nsCOMPtr.h" +#include "nsIChannel.h" +#include "nsIClassOfService.h" +#include "nsIContentPolicy.h" +#include "nsIHttpChannel.h" +#include "nsILoadInfo.h" +#include "nsIReferrerInfo.h" +#include "nsISupportsPriority.h" +#include "nsIURI.h" +#include "nsNetUtil.h" + +namespace mozilla { + +/* static */ void FontLoaderUtils::BuildChannelFlags( + nsIURI* aURI, bool aIsPreload, + nsContentSecurityManager::CORSSecurityMapping& aCorsMapping, + nsSecurityFlags& aSecurityFlags, nsContentPolicyType& aContentPolicyType) { + // aCORSMode is ignored. We always load as crossorigin=anonymous, but a + // preload started with anything other then "anonymous" will never be found. + aCorsMapping = + aURI->SchemeIs("file") + ? nsContentSecurityManager::CORSSecurityMapping:: + CORS_NONE_MAPS_TO_INHERITED_CONTEXT + : nsContentSecurityManager::CORSSecurityMapping::REQUIRE_CORS_CHECKS; + + aSecurityFlags = nsContentSecurityManager::ComputeSecurityFlags( + CORSMode::CORS_NONE, aCorsMapping); + + aContentPolicyType = aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD + : nsIContentPolicy::TYPE_FONT; +} + +/* static */ nsresult FontLoaderUtils::BuildChannelSetup( + nsIChannel* aChannel, nsIHttpChannel* aHttpChannel, + nsIReferrerInfo* aReferrerInfo, const gfxFontFaceSrc* aFontFaceSrc, + int32_t aSupportsPriorityValue) { + if (aHttpChannel) { + nsresult rv = aHttpChannel->SetRequestHeader( + "Accept"_ns, + "application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8"_ns, + false); + NS_ENSURE_SUCCESS(rv, rv); + + if (aReferrerInfo) { + rv = aHttpChannel->SetReferrerInfoWithoutClone(aReferrerInfo); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } else { + MOZ_ASSERT(aFontFaceSrc); + + rv = aHttpChannel->SetReferrerInfo(aFontFaceSrc->mReferrerInfo); + Unused << NS_WARN_IF(NS_FAILED(rv)); + + // For WOFF and WOFF2, we should tell servers/proxies/etc NOT to try + // and apply additional compression at the content-encoding layer + if (aFontFaceSrc->mFormatHint == StyleFontFaceSourceFormatKeyword::Woff || + aFontFaceSrc->mFormatHint == + StyleFontFaceSourceFormatKeyword::Woff2) { + rv = aHttpChannel->SetRequestHeader("Accept-Encoding"_ns, "identity"_ns, + false); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + + nsCOMPtr priorityChannel(do_QueryInterface(aChannel)); + if (priorityChannel) { + priorityChannel->SetPriority(aSupportsPriorityValue); + } + nsCOMPtr cos(do_QueryInterface(aChannel)); + if (cos) { + cos->AddClassFlags(nsIClassOfService::TailForbidden); + } + + return NS_OK; +} + +// static +nsresult FontLoaderUtils::BuildChannel( + nsIChannel** aChannel, nsIURI* aURI, const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, + gfxUserFontEntry* aUserFontEntry, const gfxFontFaceSrc* aFontFaceSrc, + dom::Document* aDocument, nsILoadGroup* aLoadGroup, + nsIInterfaceRequestor* aCallbacks, bool aIsPreload, + int32_t aSupportsPriorityValue) { + nsresult rv; + + nsIPrincipal* principal = + aUserFontEntry ? (aUserFontEntry->GetPrincipal() + ? aUserFontEntry->GetPrincipal()->NodePrincipal() + : nullptr) + : aDocument->NodePrincipal(); + + nsContentSecurityManager::CORSSecurityMapping corsMapping; + nsSecurityFlags securityFlags; + nsContentPolicyType contentPolicyType; + BuildChannelFlags(aURI, aIsPreload, corsMapping, securityFlags, + contentPolicyType); + + nsCOMPtr channel; + // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a + // node and a principal. This is because the document where the font is + // being loaded might have a different origin from the principal of the + // stylesheet that initiated the font load. + rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel), aURI, + aDocument, principal, securityFlags, + contentPolicyType, + nullptr, // PerformanceStorage + aLoadGroup); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr httpChannel(do_QueryInterface(channel)); + nsCOMPtr referrerInfo; + if (httpChannel && !aFontFaceSrc) { + referrerInfo = new dom::ReferrerInfo(aDocument->GetDocumentURIAsReferrer(), + aReferrerPolicy); + rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } + + rv = BuildChannelSetup(channel, httpChannel, referrerInfo, aFontFaceSrc, + aSupportsPriorityValue); + NS_ENSURE_SUCCESS(rv, rv); + + channel.forget(aChannel); + return NS_OK; +} + +// static +nsresult FontLoaderUtils::BuildChannel( + nsIChannel** aChannel, nsIURI* aURI, const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, + gfxUserFontEntry* aUserFontEntry, const gfxFontFaceSrc* aFontFaceSrc, + dom::WorkerPrivate* aWorkerPrivate, nsILoadGroup* aLoadGroup, + nsIInterfaceRequestor* aCallbacks) { + nsresult rv; + + nsIPrincipal* principal = + aUserFontEntry ? (aUserFontEntry->GetPrincipal() + ? aUserFontEntry->GetPrincipal()->NodePrincipal() + : nullptr) + : aWorkerPrivate->GetPrincipal(); + + nsContentSecurityManager::CORSSecurityMapping corsMapping; + nsSecurityFlags securityFlags; + nsContentPolicyType contentPolicyType; + BuildChannelFlags(aURI, /* aIsPreload */ false, corsMapping, securityFlags, + contentPolicyType); + + nsCOMPtr channel; + rv = NS_NewChannelWithTriggeringPrincipal( + getter_AddRefs(channel), aURI, aWorkerPrivate->GetLoadingPrincipal(), + principal, securityFlags, contentPolicyType, nullptr, nullptr, + aLoadGroup); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr httpChannel(do_QueryInterface(channel)); + + nsCOMPtr referrerInfo; + if (httpChannel && !aFontFaceSrc) { + referrerInfo = + static_cast(aWorkerPrivate->GetReferrerInfo()) + ->CloneWithNewPolicy(aReferrerPolicy); + } + + rv = BuildChannelSetup(channel, httpChannel, referrerInfo, aFontFaceSrc, + nsISupportsPriority::PRIORITY_HIGH); + NS_ENSURE_SUCCESS(rv, rv); + + channel.forget(aChannel); + return NS_OK; +} + +} // namespace mozilla diff --git a/layout/style/FontLoaderUtils.h b/layout/style/FontLoaderUtils.h new file mode 100644 index 0000000000..0c6c77866f --- /dev/null +++ b/layout/style/FontLoaderUtils.h @@ -0,0 +1,66 @@ +/* -*- 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 GECKO_LAYOUT_STYLE_FONTLOADERUTILS_H_ +#define GECKO_LAYOUT_STYLE_FONTLOADERUTILS_H_ + +#include "ErrorList.h" +#include "nsContentSecurityManager.h" + +class gfxUserFontEntry; +class nsIChannel; +class nsIHttpChannel; +class nsIInterfaceRequestor; +class nsILoadGroup; +class nsIURI; +struct gfxFontFaceSrc; + +namespace mozilla { +enum CORSMode : uint8_t; +namespace dom { +class Document; +class WorkerPrivate; +enum class ReferrerPolicy : uint8_t; +} // namespace dom + +class FontLoaderUtils { + public: + // @param aSuppportsPriorityValue See . + static nsresult BuildChannel(nsIChannel** aChannel, nsIURI* aURI, + const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, + gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc, + dom::Document* aDocument, + nsILoadGroup* aLoadGroup, + nsIInterfaceRequestor* aCallbacks, + bool aIsPreload, int32_t aSupportsPriorityValue); + + static nsresult BuildChannel(nsIChannel** aChannel, nsIURI* aURI, + const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, + gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc, + dom::WorkerPrivate* aWorkerPrivate, + nsILoadGroup* aLoadGroup, + nsIInterfaceRequestor* aCallbacks); + + private: + static void BuildChannelFlags( + nsIURI* aURI, bool aIsPreload, + nsContentSecurityManager::CORSSecurityMapping& aCorsMapping, + nsSecurityFlags& aSecurityFlags, nsContentPolicyType& aContentPolicyType); + + static nsresult BuildChannelSetup(nsIChannel* aChannel, + nsIHttpChannel* aHttpChannel, + nsIReferrerInfo* aReferrerInfo, + const gfxFontFaceSrc* aFontFaceSrc, + int32_t aSupportsPriorityValue); +}; + +} // namespace mozilla + +#endif // GECKO_LAYOUT_STYLE_FONTLOADERUTILS_H_ diff --git a/layout/style/FontPreloader.cpp b/layout/style/FontPreloader.cpp new file mode 100644 index 0000000000..564d6004da --- /dev/null +++ b/layout/style/FontPreloader.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 "FontPreloader.h" + +#include "mozilla/FontLoaderUtils.h" +#include "gfxPlatform.h" + +namespace mozilla { + +FontPreloader::FontPreloader() + : FetchPreloader(nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD) {} + +nsresult FontPreloader::CreateChannel( + nsIChannel** aChannel, nsIURI* aURI, const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, dom::Document* aDocument, + nsILoadGroup* aLoadGroup, nsIInterfaceRequestor* aCallbacks, + uint64_t aEarlyHintPreloaderId, int32_t aSupportsPriorityValue) { + // Don't preload fonts if they've been preffed-off. + if (!gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) { + return NS_ERROR_NOT_AVAILABLE; + } + + return FontLoaderUtils::BuildChannel( + aChannel, aURI, aCORSMode, aReferrerPolicy, nullptr, nullptr, aDocument, + aLoadGroup, aCallbacks, true, aSupportsPriorityValue); +} + +} // namespace mozilla diff --git a/layout/style/FontPreloader.h b/layout/style/FontPreloader.h new file mode 100644 index 0000000000..23e4cc38da --- /dev/null +++ b/layout/style/FontPreloader.h @@ -0,0 +1,30 @@ +/* -*- 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 FontPreloader_h_ +#define FontPreloader_h_ + +#include "mozilla/FetchPreloader.h" + +namespace mozilla { + +class FontPreloader final : public FetchPreloader { + public: + FontPreloader(); + + protected: + nsresult CreateChannel(nsIChannel** aChannel, nsIURI* aURI, + const CORSMode aCORSMode, + const dom::ReferrerPolicy& aReferrerPolicy, + dom::Document* aDocument, nsILoadGroup* aLoadGroup, + nsIInterfaceRequestor* aCallbacks, + uint64_t aEarlyHintPreloaderId, + int32_t aSupportsPriorityValue) override; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/GeckoBindings.cpp b/layout/style/GeckoBindings.cpp new file mode 100644 index 0000000000..5c7992f8db --- /dev/null +++ b/layout/style/GeckoBindings.cpp @@ -0,0 +1,1819 @@ +/* -*- 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/. */ + +/* FFI functions for Servo to call into Gecko */ + +#include "mozilla/GeckoBindings.h" + +#include "ChildIterator.h" +#include "ErrorReporter.h" +#include "gfxFontFeatures.h" +#include "gfxMathTable.h" +#include "gfxTextRun.h" +#include "imgLoader.h" +#include "nsAnimationManager.h" +#include "nsAttrValueInlines.h" +#include "nsCSSFrameConstructor.h" +#include "nsCSSProps.h" +#include "nsCSSPseudoElements.h" +#include "nsContentUtils.h" +#include "nsDOMTokenList.h" +#include "nsDeviceContext.h" +#include "nsLayoutUtils.h" +#include "nsIContentInlines.h" +#include "mozilla/dom/DocumentInlines.h" +#include "nsILoadContext.h" +#include "nsIFrame.h" +#include "nsIMozBrowserFrame.h" +#include "nsINode.h" +#include "nsIURI.h" +#include "nsFontMetrics.h" +#include "nsNameSpaceManager.h" +#include "nsNetUtil.h" +#include "nsProxyRelease.h" +#include "nsString.h" +#include "nsStyleStruct.h" +#include "nsStyleUtil.h" +#include "nsTArray.h" +#include "nsTransitionManager.h" +#include "nsWindowSizes.h" + +#include "mozilla/css/ImageLoader.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/AttributeStyles.h" +#include "mozilla/EffectCompositor.h" +#include "mozilla/EffectSet.h" +#include "mozilla/FontPropertyTypes.h" +#include "mozilla/Hal.h" +#include "mozilla/Keyframe.h" +#include "mozilla/Mutex.h" +#include "mozilla/Preferences.h" +#include "mozilla/ServoElementSnapshot.h" +#include "mozilla/ShadowParts.h" +#include "mozilla/StaticPresData.h" +#include "mozilla/StaticPrefs_browser.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/RestyleManager.h" +#include "mozilla/SizeOfState.h" +#include "mozilla/StyleAnimationValue.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/ServoTraversalStatistics.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimelineManager.h" +#include "mozilla/RWLock.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ElementInlines.h" +#include "mozilla/dom/HTMLImageElement.h" +#include "mozilla/dom/HTMLTableCellElement.h" +#include "mozilla/dom/HTMLBodyElement.h" +#include "mozilla/dom/HTMLSelectElement.h" +#include "mozilla/dom/HTMLSlotElement.h" +#include "mozilla/dom/MediaList.h" +#include "mozilla/dom/ReferrerInfo.h" +#include "mozilla/dom/SVGElement.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/URLExtraData.h" +#include "mozilla/dom/CSSMozDocumentRule.h" + +#if defined(MOZ_MEMORY) +# include "mozmemory.h" +#endif + +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +// Definitions of the global traversal stats. +bool ServoTraversalStatistics::sActive = false; +ServoTraversalStatistics ServoTraversalStatistics::sSingleton; + +static StaticAutoPtr sServoFFILock; + +static const LangGroupFontPrefs* ThreadSafeGetLangGroupFontPrefs( + const Document& aDocument, nsAtom* aLanguage) { + bool needsCache = false; + { + AutoReadLock guard(*sServoFFILock); + if (auto* prefs = aDocument.GetFontPrefsForLang(aLanguage, &needsCache)) { + return prefs; + } + } + MOZ_ASSERT(needsCache); + AutoWriteLock guard(*sServoFFILock); + return aDocument.GetFontPrefsForLang(aLanguage); +} + +static const nsFont& ThreadSafeGetDefaultVariableFont(const Document& aDocument, + nsAtom* aLanguage) { + return ThreadSafeGetLangGroupFontPrefs(aDocument, aLanguage) + ->mDefaultVariableFont; +} + +/* + * Does this child count as significant for selector matching? + * + * See nsStyleUtil::IsSignificantChild for details. + */ +bool Gecko_IsSignificantChild(const nsINode* aNode, + bool aWhitespaceIsSignificant) { + return nsStyleUtil::ThreadSafeIsSignificantChild(aNode->AsContent(), + aWhitespaceIsSignificant); +} + +const nsINode* Gecko_GetLastChild(const nsINode* aNode) { + return aNode->GetLastChild(); +} + +const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode* aNode) { + return aNode->GetFlattenedTreeParentNodeForStyle(); +} + +const Element* Gecko_GetBeforeOrAfterPseudo(const Element* aElement, + bool aIsBefore) { + MOZ_ASSERT(aElement); + MOZ_ASSERT(aElement->HasProperties()); + + return aIsBefore ? nsLayoutUtils::GetBeforePseudo(aElement) + : nsLayoutUtils::GetAfterPseudo(aElement); +} + +const Element* Gecko_GetMarkerPseudo(const Element* aElement) { + MOZ_ASSERT(aElement); + MOZ_ASSERT(aElement->HasProperties()); + + return nsLayoutUtils::GetMarkerPseudo(aElement); +} + +nsTArray* Gecko_GetAnonymousContentForElement( + const Element* aElement) { + nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame()); + if (!ac) { + return nullptr; + } + + auto* array = new nsTArray(); + nsContentUtils::AppendNativeAnonymousChildren(aElement, *array, 0); + return array; +} + +void Gecko_DestroyAnonymousContentList(nsTArray* aAnonContent) { + MOZ_ASSERT(aAnonContent); + delete aAnonContent; +} + +const nsTArray>* Gecko_GetAssignedNodes( + const Element* aElement) { + MOZ_ASSERT(HTMLSlotElement::FromNode(aElement)); + return &static_cast(aElement)->AssignedNodes(); +} + +void Gecko_GetQueryContainerSize(const Element* aElement, nscoord* aOutWidth, + nscoord* aOutHeight) { + MOZ_ASSERT(aElement); + const nsIFrame* frame = aElement->GetPrimaryFrame(); + if (!frame) { + return; + } + const auto containAxes = frame->GetContainSizeAxes(); + if (!containAxes.IsAny()) { + return; + } + nsSize size = frame->GetContentRectRelativeToSelf().Size(); + bool isVertical = frame->GetWritingMode().IsVertical(); + if (isVertical ? containAxes.mBContained : containAxes.mIContained) { + *aOutWidth = size.width; + } + if (isVertical ? containAxes.mIContained : containAxes.mBContained) { + *aOutHeight = size.height; + } +} + +void Gecko_ComputedStyle_Init(ComputedStyle* aStyle, + const ServoComputedData* aValues, + PseudoStyleType aPseudoType) { + new (KnownNotNull, aStyle) + ComputedStyle(aPseudoType, ServoComputedDataForgotten(aValues)); +} + +ServoComputedData::ServoComputedData(const ServoComputedDataForgotten aValue) { + PodAssign(this, aValue.mPtr); +} + +MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleStructsMallocEnclosingSizeOf) + +void ServoComputedData::AddSizeOfExcludingThis(nsWindowSizes& aSizes) const { + // Note: GetStyleFoo() returns a pointer to an nsStyleFoo that sits within a + // servo_arc::Arc, i.e. it is preceded by a word-sized refcount. So we need + // to measure it with a function that can handle an interior pointer. We use + // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's + // output the memory measured here. +#define STYLE_STRUCT(name_) \ + static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \ + "alignment will break AddSizeOfExcludingThis()"); \ + const void* p##name_ = Style##name_(); \ + if (!aSizes.mState.HaveSeenPtr(p##name_)) { \ + aSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \ + ServoStyleStructsMallocEnclosingSizeOf(p##name_); \ + } +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + + if (visited_style && !aSizes.mState.HaveSeenPtr(visited_style)) { + visited_style->AddSizeOfIncludingThis(aSizes, + &aSizes.mLayoutComputedValuesVisited); + } + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - custom_properties + // - writing_mode + // - rules + // - font_computation_data +} + +void Gecko_ComputedStyle_Destroy(ComputedStyle* aStyle) { + aStyle->~ComputedStyle(); +} + +void Gecko_ConstructStyleChildrenIterator(const Element* aElement, + StyleChildrenIterator* aIterator) { + MOZ_ASSERT(aElement); + MOZ_ASSERT(aIterator); + new (aIterator) StyleChildrenIterator(aElement); +} + +void Gecko_DestroyStyleChildrenIterator(StyleChildrenIterator* aIterator) { + MOZ_ASSERT(aIterator); + + aIterator->~StyleChildrenIterator(); +} + +const nsINode* Gecko_GetNextStyleChild(StyleChildrenIterator* aIterator) { + MOZ_ASSERT(aIterator); + return aIterator->GetNextChild(); +} + +bool Gecko_VisitedStylesEnabled(const Document* aDoc) { + MOZ_ASSERT(aDoc); + MOZ_ASSERT(NS_IsMainThread()); + + if (!StaticPrefs::layout_css_visited_links_enabled()) { + return false; + } + + if (aDoc->IsBeingUsedAsImage()) { + return false; + } + + nsILoadContext* loadContext = aDoc->GetLoadContext(); + if (loadContext && loadContext->UsePrivateBrowsing()) { + return false; + } + + return true; +} + +ElementState::InternalType Gecko_ElementState(const Element* aElement) { + return aElement->StyleState().GetInternalValue(); +} + +bool Gecko_IsRootElement(const Element* aElement) { + return aElement->OwnerDoc()->GetRootElement() == aElement; +} + +void Gecko_NoteDirtyElement(const Element* aElement) { + MOZ_ASSERT(NS_IsMainThread()); + const_cast(aElement)->NoteDirtyForServo(); +} + +void Gecko_NoteDirtySubtreeForInvalidation(const Element* aElement) { + MOZ_ASSERT(NS_IsMainThread()); + const_cast(aElement)->NoteDirtySubtreeForServo(); +} + +void Gecko_NoteAnimationOnlyDirtyElement(const Element* aElement) { + MOZ_ASSERT(NS_IsMainThread()); + const_cast(aElement)->NoteAnimationOnlyDirtyForServo(); +} + +bool Gecko_AnimationNameMayBeReferencedFromStyle( + const nsPresContext* aPresContext, nsAtom* aName) { + MOZ_ASSERT(aPresContext); + return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName); +} + +float Gecko_GetScrollbarInlineSize(const nsPresContext* aPc) { + MOZ_ASSERT(aPc); + AutoWriteLock guard(*sServoFFILock); // We read some look&feel values. + auto overlay = aPc->UseOverlayScrollbars() ? nsITheme::Overlay::Yes + : nsITheme::Overlay::No; + LayoutDeviceIntCoord size = + aPc->Theme()->GetScrollbarSize(aPc, StyleScrollbarWidth::Auto, overlay); + return aPc->DevPixelsToFloatCSSPixels(size); +} + +PseudoStyleType Gecko_GetImplementedPseudo(const Element* aElement) { + return aElement->GetPseudoElementType(); +} + +uint32_t Gecko_CalcStyleDifference(const ComputedStyle* aOldStyle, + const ComputedStyle* aNewStyle, + bool* aAnyStyleStructChanged, + bool* aOnlyResetStructsChanged) { + MOZ_ASSERT(aOldStyle); + MOZ_ASSERT(aNewStyle); + + uint32_t equalStructs; + nsChangeHint result = + aOldStyle->CalcStyleDifference(*aNewStyle, &equalStructs); + + *aAnyStyleStructChanged = + equalStructs != StyleStructConstants::kAllStructsMask; + + const auto kInheritedStructsMask = + StyleStructConstants::kInheritedStructsMask; + *aOnlyResetStructsChanged = + (equalStructs & kInheritedStructsMask) == kInheritedStructsMask; + + return result; +} + +nscoord Gecko_CalcLineHeight(const StyleLineHeight* aLh, + const nsPresContext* aPc, bool aVertical, + const nsStyleFont* aAgainstFont, + const mozilla::dom::Element* aElement) { + // Normal line-height depends on font metrics. + AutoWriteLock guard(*sServoFFILock); + return ReflowInput::CalcLineHeight(*aLh, *aAgainstFont, + const_cast(aPc), aVertical, + aElement, NS_UNCONSTRAINEDSIZE, 1.0f); +} + +const ServoElementSnapshot* Gecko_GetElementSnapshot( + const ServoElementSnapshotTable* aTable, const Element* aElement) { + MOZ_ASSERT(aTable); + MOZ_ASSERT(aElement); + + return aTable->Get(const_cast(aElement)); +} + +bool Gecko_HaveSeenPtr(SeenPtrs* aTable, const void* aPtr) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTable); + // Empty Rust allocations are indicated by small values up to the alignment + // of the relevant type. We shouldn't see anything like that here. + MOZ_ASSERT(uintptr_t(aPtr) > 16); + + return aTable->HaveSeenPtr(aPtr); +} + +const StyleLockedDeclarationBlock* Gecko_GetStyleAttrDeclarationBlock( + const Element* aElement) { + DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); + if (!decl) { + return nullptr; + } + return decl->Raw(); +} + +void Gecko_UnsetDirtyStyleAttr(const Element* aElement) { + DeclarationBlock* decl = aElement->GetInlineStyleDeclaration(); + if (!decl) { + return; + } + decl->UnsetDirty(); +} + +const StyleLockedDeclarationBlock* +Gecko_GetHTMLPresentationAttrDeclarationBlock(const Element* aElement) { + return aElement->GetMappedAttributeStyle(); +} + +const StyleLockedDeclarationBlock* Gecko_GetExtraContentStyleDeclarations( + const Element* aElement) { + if (const auto* cell = HTMLTableCellElement::FromNode(aElement)) { + return cell->GetMappedAttributesInheritedFromTable(); + } + if (const auto* img = HTMLImageElement::FromNode(aElement)) { + return img->GetMappedAttributesFromSource(); + } + return nullptr; +} + +const StyleLockedDeclarationBlock* Gecko_GetUnvisitedLinkAttrDeclarationBlock( + const Element* aElement) { + AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); + if (!attrStyles) { + return nullptr; + } + + return attrStyles->GetServoUnvisitedLinkDecl(); +} + +StyleSheet* Gecko_StyleSheet_Clone(const StyleSheet* aSheet, + const StyleSheet* aNewParentSheet) { + MOZ_ASSERT(aSheet); + MOZ_ASSERT(aSheet->GetParentSheet(), "Should only be used for @import"); + MOZ_ASSERT(aNewParentSheet, "Wat"); + + RefPtr newSheet = aSheet->Clone(nullptr, nullptr); + + // NOTE(emilio): This code runs in the StylesheetInner constructor, which + // means that the inner pointer of `aNewParentSheet` still points to the old + // one. + // + // So we _don't_ update neither the parent pointer of the stylesheet, nor the + // child list (yet). This is fixed up in that same constructor. + return static_cast(newSheet.forget().take()); +} + +void Gecko_StyleSheet_AddRef(const StyleSheet* aSheet) { + MOZ_ASSERT(NS_IsMainThread()); + const_cast(aSheet)->AddRef(); +} + +void Gecko_StyleSheet_Release(const StyleSheet* aSheet) { + MOZ_ASSERT(NS_IsMainThread()); + const_cast(aSheet)->Release(); +} + +const StyleLockedDeclarationBlock* Gecko_GetVisitedLinkAttrDeclarationBlock( + const Element* aElement) { + AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); + if (!attrStyles) { + return nullptr; + } + return attrStyles->GetServoVisitedLinkDecl(); +} + +const StyleLockedDeclarationBlock* Gecko_GetActiveLinkAttrDeclarationBlock( + const Element* aElement) { + AttributeStyles* attrStyles = aElement->OwnerDoc()->GetAttributeStyles(); + if (!attrStyles) { + return nullptr; + } + return attrStyles->GetServoActiveLinkDecl(); +} + +bool Gecko_GetAnimationRule(const Element* aElement, + EffectCompositor::CascadeLevel aCascadeLevel, + StyleAnimationValueMap* aAnimationValues) { + MOZ_ASSERT(aElement); + + Document* doc = aElement->GetComposedDoc(); + if (!doc) { + return false; + } + nsPresContext* presContext = doc->GetPresContext(); + if (!presContext) { + return false; + } + + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + return presContext->EffectCompositor()->GetServoAnimationRule( + element, pseudoType, aCascadeLevel, aAnimationValues); +} + +bool Gecko_StyleAnimationsEquals(const nsStyleAutoArray* aA, + const nsStyleAutoArray* aB) { + return *aA == *aB; +} + +bool Gecko_StyleScrollTimelinesEquals( + const nsStyleAutoArray* aA, + const nsStyleAutoArray* aB) { + return *aA == *aB; +} + +bool Gecko_StyleViewTimelinesEquals( + const nsStyleAutoArray* aA, + const nsStyleAutoArray* aB) { + return *aA == *aB; +} + +void Gecko_UpdateAnimations(const Element* aElement, + const ComputedStyle* aOldComputedData, + const ComputedStyle* aComputedData, + UpdateAnimationsTasks aTasks) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aElement); + + if (!aElement->IsInComposedDoc()) { + return; + } + + nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement); + if (!presContext || !presContext->IsDynamic()) { + return; + } + + nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); + + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + + // Handle scroll/view timelines first because CSS animations may refer to the + // timeline defined by itself. + if (aTasks & UpdateAnimationsTasks::ScrollTimelines) { + presContext->TimelineManager()->UpdateTimelines( + const_cast(element), pseudoType, aComputedData, + TimelineManager::ProgressTimelineType::Scroll); + } + + if (aTasks & UpdateAnimationsTasks::ViewTimelines) { + presContext->TimelineManager()->UpdateTimelines( + const_cast(element), pseudoType, aComputedData, + TimelineManager::ProgressTimelineType::View); + } + + if (aTasks & UpdateAnimationsTasks::CSSAnimations) { + presContext->AnimationManager()->UpdateAnimations( + const_cast(element), pseudoType, aComputedData); + } + + // aComputedData might be nullptr if the target element is now in a + // display:none subtree. We still call Gecko_UpdateAnimations in this case + // because we need to stop CSS animations in the display:none subtree. + // However, we don't need to update transitions since they are stopped by + // RestyleManager::AnimationsWithDestroyedFrame so we just return early + // here. + if (!aComputedData) { + return; + } + + if (aTasks & UpdateAnimationsTasks::CSSTransitions) { + MOZ_ASSERT(aOldComputedData); + presContext->TransitionManager()->UpdateTransitions( + const_cast(element), pseudoType, *aOldComputedData, + *aComputedData); + } + + if (aTasks & UpdateAnimationsTasks::EffectProperties) { + presContext->EffectCompositor()->UpdateEffectProperties( + aComputedData, const_cast(element), pseudoType); + } + + if (aTasks & UpdateAnimationsTasks::CascadeResults) { + EffectSet* effectSet = EffectSet::Get(element, pseudoType); + // CSS animations/transitions might have been destroyed as part of the above + // steps so before updating cascade results, we check if there are still any + // animations to update. + if (effectSet) { + // We call UpdateCascadeResults directly (intead of + // MaybeUpdateCascadeResults) since we know for sure that the cascade has + // changed, but we were unable to call MarkCascadeUpdated when we noticed + // it since we avoid mutating state as part of the Servo parallel + // traversal. + presContext->EffectCompositor()->UpdateCascadeResults( + *effectSet, const_cast(element), pseudoType); + } + } + + if (aTasks & UpdateAnimationsTasks::DisplayChangedFromNone) { + presContext->EffectCompositor()->RequestRestyle( + const_cast(element), pseudoType, + EffectCompositor::RestyleType::Standard, + EffectCompositor::CascadeLevel::Animations); + } +} + +size_t Gecko_GetAnimationEffectCount(const Element* aElementOrPseudo) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElementOrPseudo); + + EffectSet* effectSet = EffectSet::Get(element, pseudoType); + return effectSet ? effectSet->Count() : 0; +} + +bool Gecko_ElementHasAnimations(const Element* aElement) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + return !!EffectSet::Get(element, pseudoType); +} + +bool Gecko_ElementHasCSSAnimations(const Element* aElement) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + auto* collection = + nsAnimationManager::CSSAnimationCollection::Get(element, pseudoType); + return collection && !collection->mAnimations.IsEmpty(); +} + +bool Gecko_ElementHasCSSTransitions(const Element* aElement) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + auto* collection = + nsTransitionManager::CSSTransitionCollection::Get(element, pseudoType); + return collection && !collection->mAnimations.IsEmpty(); +} + +size_t Gecko_ElementTransitions_Length(const Element* aElement) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + auto* collection = + nsTransitionManager::CSSTransitionCollection::Get(element, pseudoType); + return collection ? collection->mAnimations.Length() : 0; +} + +static CSSTransition* GetCurrentTransitionAt(const Element* aElement, + size_t aIndex) { + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(aElement); + auto* collection = + nsTransitionManager::CSSTransitionCollection ::Get(element, pseudoType); + if (!collection) { + return nullptr; + } + return collection->mAnimations.SafeElementAt(aIndex); +} + +nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(const Element* aElement, + size_t aIndex) { + CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); + return transition ? transition->TransitionProperty().mID + : nsCSSPropertyID::eCSSProperty_UNKNOWN; +} + +const StyleAnimationValue* Gecko_ElementTransitions_EndValueAt( + const Element* aElement, size_t aIndex) { + CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex); + return transition ? transition->ToValue().mServo.get() : nullptr; +} + +double Gecko_GetProgressFromComputedTiming(const ComputedTiming* aTiming) { + return aTiming->mProgress.Value(); +} + +double Gecko_GetPositionInSegment(const AnimationPropertySegment* aSegment, + double aProgress, bool aBeforeFlag) { + MOZ_ASSERT(aSegment->mFromKey < aSegment->mToKey, + "The segment from key should be less than to key"); + + double positionInSegment = (aProgress - aSegment->mFromKey) / + // To avoid floating precision inaccuracies, make + // sure we calculate both the numerator and + // denominator using double precision. + (double(aSegment->mToKey) - aSegment->mFromKey); + + return StyleComputedTimingFunction::GetPortion( + aSegment->mTimingFunction, positionInSegment, aBeforeFlag); +} + +const StyleAnimationValue* Gecko_AnimationGetBaseStyle( + const RawServoAnimationValueTable* aBaseStyles, + const mozilla::AnimatedPropertyID* aProperty) { + const auto* base = reinterpret_cast, StyleAnimationValue>*>(aBaseStyles); + return base->GetWeak(*aProperty); +} + +void Gecko_FillAllImageLayers(nsStyleImageLayers* aLayers, uint32_t aMaxLen) { + aLayers->FillAllLayers(aMaxLen); +} + +bool Gecko_IsDocumentBody(const Element* aElement) { + Document* doc = aElement->GetUncomposedDoc(); + return doc && doc->GetBodyElement() == aElement; +} + +bool Gecko_IsDarkColorScheme(const Document* aDoc, + const StyleColorScheme* aStyle) { + return LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits) == + ColorScheme::Dark; +} + +nscolor Gecko_ComputeSystemColor(StyleSystemColor aColor, const Document* aDoc, + const StyleColorScheme* aStyle) { + auto colorScheme = LookAndFeel::ColorSchemeForStyle(*aDoc, aStyle->bits); + const auto& prefs = PreferenceSheet::PrefsFor(*aDoc); + if (prefs.mMustUseLightSystemColors) { + colorScheme = ColorScheme::Light; + } + const auto& colors = prefs.ColorsFor(colorScheme); + switch (aColor) { + case StyleSystemColor::Canvastext: + return colors.mDefault; + case StyleSystemColor::Canvas: + return colors.mDefaultBackground; + case StyleSystemColor::Linktext: + return colors.mLink; + case StyleSystemColor::Activetext: + return colors.mActiveLink; + case StyleSystemColor::Visitedtext: + return colors.mVisitedLink; + default: + break; + } + + auto useStandins = LookAndFeel::ShouldUseStandins(*aDoc, aColor); + + AutoWriteLock guard(*sServoFFILock); + return LookAndFeel::Color(aColor, colorScheme, useStandins); +} + +int32_t Gecko_GetLookAndFeelInt(int32_t aId) { + auto intId = static_cast(aId); + AutoWriteLock guard(*sServoFFILock); + return LookAndFeel::GetInt(intId); +} + +float Gecko_GetLookAndFeelFloat(int32_t aId) { + auto id = static_cast(aId); + AutoWriteLock guard(*sServoFFILock); + return LookAndFeel::GetFloat(id); +} + +bool Gecko_MatchLang(const Element* aElement, nsAtom* aOverrideLang, + bool aHasOverrideLang, const char16_t* aValue) { + MOZ_ASSERT(!(aOverrideLang && !aHasOverrideLang), + "aHasOverrideLang should only be set when aOverrideLang is null"); + MOZ_ASSERT(aValue, "null lang parameter"); + if (!aValue || !*aValue) { + return false; + } + + // We have to determine the language of the current element. Since + // this is currently no property and since the language is inherited + // from the parent we have to be prepared to look at all parent + // nodes. The language itself is encoded in the LANG attribute. + if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) { + return nsStyleUtil::LangTagCompare(nsAtomCString(language), + NS_ConvertUTF16toUTF8(aValue)); + } + + // Try to get the language from the HTTP header or if this + // is missing as well from the preferences. + // The content language can be a comma-separated list of + // language codes. + // FIXME: We're not really consistent in our treatment of comma-separated + // content-language values. + if (nsAtom* language = aElement->OwnerDoc()->GetContentLanguage()) { + const NS_ConvertUTF16toUTF8 langString(aValue); + nsAtomCString docLang(language); + docLang.StripWhitespace(); + for (auto const& lang : docLang.Split(',')) { + if (nsStyleUtil::LangTagCompare(lang, langString)) { + return true; + } + } + } + return false; +} + +nsAtom* Gecko_GetXMLLangValue(const Element* aElement) { + const nsAttrValue* attr = + aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); + + if (!attr) { + return nullptr; + } + + MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); + + RefPtr atom = attr->GetAtomValue(); + return atom.forget().take(); +} + +const PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs(const Document* aDoc) { + return &PreferenceSheet::PrefsFor(*aDoc); +} + +bool Gecko_IsTableBorderNonzero(const Element* aElement) { + if (!aElement->IsHTMLElement(nsGkAtoms::table)) { + return false; + } + const nsAttrValue* val = aElement->GetParsedAttr(nsGkAtoms::border); + return val && + (val->Type() != nsAttrValue::eInteger || val->GetIntegerValue() != 0); +} + +bool Gecko_IsSelectListBox(const Element* aElement) { + const auto* select = HTMLSelectElement::FromNode(aElement); + return select && !select->IsCombobox(); +} + +template +static nsAtom* LangValue(Implementor* aElement) { + // TODO(emilio): Deduplicate a bit with nsIContent::GetLang(). + const nsAttrValue* attr = + aElement->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML); + if (!attr && aElement->SupportsLangAttr()) { + attr = aElement->GetParsedAttr(nsGkAtoms::lang); + } + + if (!attr) { + return nullptr; + } + + MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom); + RefPtr atom = attr->GetAtomValue(); + return atom.forget().take(); +} + +bool Gecko_AttrEquals(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + return aValue->Equals(aStr, aIgnoreCase ? eIgnoreCase : eCaseMatters); +} + +#define WITH_COMPARATOR(ignore_case_, c_, expr_) \ + auto c_ = (ignore_case_) ? nsASCIICaseInsensitiveStringComparator \ + : nsTDefaultStringComparator; \ + return expr_; + +bool Gecko_AttrDashEquals(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + nsAutoString str; + aValue->ToString(str); + WITH_COMPARATOR( + aIgnoreCase, c, + nsStyleUtil::DashMatchCompare(str, nsDependentAtomString(aStr), c)) +} + +bool Gecko_AttrIncludes(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + if (aStr == nsGkAtoms::_empty) { + return false; + } + nsAutoString str; + aValue->ToString(str); + WITH_COMPARATOR( + aIgnoreCase, c, + nsStyleUtil::ValueIncludes(str, nsDependentAtomString(aStr), c)) +} + +bool Gecko_AttrHasSubstring(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + return aStr != nsGkAtoms::_empty && + aValue->HasSubstring(nsDependentAtomString(aStr), + aIgnoreCase ? eIgnoreCase : eCaseMatters); +} + +bool Gecko_AttrHasPrefix(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + return aStr != nsGkAtoms::_empty && + aValue->HasPrefix(nsDependentAtomString(aStr), + aIgnoreCase ? eIgnoreCase : eCaseMatters); +} + +bool Gecko_AttrHasSuffix(const nsAttrValue* aValue, const nsAtom* aStr, + bool aIgnoreCase) { + return aStr != nsGkAtoms::_empty && + aValue->HasSuffix(nsDependentAtomString(aStr), + aIgnoreCase ? eIgnoreCase : eCaseMatters); +} + +#define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \ + nsAtom* prefix_##LangValue(implementor_ aElement) { \ + return LangValue(aElement); \ + } + +SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, const Element*) +SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, + const ServoElementSnapshot*) + +#undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS + +nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength) { + return NS_Atomize(nsDependentCSubstring(aString, aLength)).take(); +} + +nsAtom* Gecko_Atomize16(const nsAString* aString) { + return NS_Atomize(*aString).take(); +} + +void Gecko_AddRefAtom(nsAtom* aAtom) { NS_ADDREF(aAtom); } + +void Gecko_ReleaseAtom(nsAtom* aAtom) { NS_RELEASE(aAtom); } + +void Gecko_nsFont_InitSystem(nsFont* aDest, StyleSystemFont aFontId, + const nsStyleFont* aFont, + const Document* aDocument) { + const nsFont& defaultVariableFont = + ThreadSafeGetDefaultVariableFont(*aDocument, aFont->mLanguage); + + // We have passed uninitialized memory to this function, + // initialize it. We can't simply return an nsFont because then + // we need to know its size beforehand. Servo cannot initialize nsFont + // itself, so this will do. + new (aDest) nsFont(defaultVariableFont); + + AutoWriteLock guard(*sServoFFILock); + nsLayoutUtils::ComputeSystemFont(aDest, aFontId, defaultVariableFont, + aDocument); +} + +void Gecko_nsFont_Destroy(nsFont* aDest) { aDest->~nsFont(); } + +StyleGenericFontFamily Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage( + const Document* aDoc, nsAtom* aLanguage) { + return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLanguage)->GetDefaultGeneric(); +} + +Length Gecko_GetBaseSize(const Document* aDoc, nsAtom* aLang, + StyleGenericFontFamily aGeneric) { + return ThreadSafeGetLangGroupFontPrefs(*aDoc, aLang) + ->GetDefaultFont(aGeneric) + ->size; +} + +gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet() { + return new gfxFontFeatureValueSet(); +} + +nsTArray* Gecko_AppendFeatureValueHashEntry( + gfxFontFeatureValueSet* aFontFeatureValues, nsAtom* aFamily, + uint32_t aAlternate, nsAtom* aName) { + MOZ_ASSERT(NS_IsMainThread()); + return aFontFeatureValues->AppendFeatureValueHashEntry(nsAtomCString(aFamily), + aName, aAlternate); +} + +gfx::FontPaletteValueSet* Gecko_ConstructFontPaletteValueSet() { + return new gfx::FontPaletteValueSet(); +} + +gfx::FontPaletteValueSet::PaletteValues* Gecko_AppendPaletteValueHashEntry( + gfx::FontPaletteValueSet* aPaletteValueSet, nsAtom* aFamily, + nsAtom* aName) { + MOZ_ASSERT(NS_IsMainThread()); + return aPaletteValueSet->Insert(aName, nsAtomCString(aFamily)); +} + +void Gecko_SetFontPaletteBase(gfx::FontPaletteValueSet::PaletteValues* aValues, + int32_t aBasePaletteIndex) { + aValues->mBasePalette = aBasePaletteIndex; +} + +void Gecko_SetFontPaletteOverride( + gfx::FontPaletteValueSet::PaletteValues* aValues, int32_t aIndex, + StyleAbsoluteColor* aColor) { + if (aIndex < 0) { + return; + } + aValues->mOverrides.AppendElement(gfx::FontPaletteValueSet::OverrideColor{ + uint32_t(aIndex), gfx::sRGBColor::FromABGR(aColor->ToColor())}); +} + +void Gecko_CounterStyle_ToPtr(const StyleCounterStyle* aStyle, + CounterStylePtr* aPtr) { + *aPtr = CounterStylePtr::FromStyle(*aStyle); +} + +void Gecko_SetCounterStyleToNone(CounterStylePtr* aPtr) { + *aPtr = nsGkAtoms::none; +} + +void Gecko_SetCounterStyleToString(CounterStylePtr* aPtr, + const nsACString* aSymbol) { + *aPtr = new AnonymousCounterStyle(NS_ConvertUTF8toUTF16(*aSymbol)); +} + +void Gecko_CopyCounterStyle(CounterStylePtr* aDst, + const CounterStylePtr* aSrc) { + *aDst = *aSrc; +} + +nsAtom* Gecko_CounterStyle_GetName(const CounterStylePtr* aPtr) { + return aPtr->IsAtom() ? aPtr->AsAtom() : nullptr; +} + +const AnonymousCounterStyle* Gecko_CounterStyle_GetAnonymous( + const CounterStylePtr* aPtr) { + return aPtr->AsAnonymous(); +} + +void Gecko_EnsureTArrayCapacity(void* aArray, size_t aCapacity, + size_t aElemSize) { + auto base = + reinterpret_cast*>(aArray); + + base->EnsureCapacity(aCapacity, aElemSize); +} + +void Gecko_ClearPODTArray(void* aArray, size_t aElementSize, + size_t aElementAlign) { + auto base = + reinterpret_cast*>(aArray); + + base->template ShiftData( + 0, base->Length(), 0, aElementSize, aElementAlign); +} + +void Gecko_ResizeTArrayForStrings(nsTArray* aArray, + uint32_t aLength) { + aArray->SetLength(aLength); +} + +void Gecko_ResizeAtomArray(nsTArray>* aArray, uint32_t aLength) { + aArray->SetLength(aLength); +} + +void Gecko_EnsureImageLayersLength(nsStyleImageLayers* aLayers, size_t aLen, + nsStyleImageLayers::LayerType aLayerType) { + size_t oldLength = aLayers->mLayers.Length(); + + aLayers->mLayers.EnsureLengthAtLeast(aLen); + + for (size_t i = oldLength; i < aLen; ++i) { + aLayers->mLayers[i].Initialize(aLayerType); + } +} + +template +static void EnsureStyleAutoArrayLength(StyleType* aArray, size_t aLen) { + aArray->EnsureLengthAtLeast(aLen); +} + +void Gecko_EnsureStyleAnimationArrayLength(void* aArray, size_t aLen) { + auto* base = static_cast*>(aArray); + EnsureStyleAutoArrayLength(base, aLen); +} + +void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) { + auto* base = reinterpret_cast*>(aArray); + EnsureStyleAutoArrayLength(base, aLen); +} + +void Gecko_EnsureStyleScrollTimelineArrayLength(void* aArray, size_t aLen) { + auto* base = static_cast*>(aArray); + EnsureStyleAutoArrayLength(base, aLen); +} + +void Gecko_EnsureStyleViewTimelineArrayLength(void* aArray, size_t aLen) { + auto* base = static_cast*>(aArray); + EnsureStyleAutoArrayLength(base, aLen); +} + +enum class KeyframeSearchDirection { + Forwards, + Backwards, +}; + +enum class KeyframeInsertPosition { + Prepend, + LastForOffset, +}; + +static Keyframe* GetOrCreateKeyframe( + nsTArray* aKeyframes, float aOffset, + const StyleComputedTimingFunction* aTimingFunction, + const CompositeOperationOrAuto aComposition, + KeyframeSearchDirection aSearchDirection, + KeyframeInsertPosition aInsertPosition) { + MOZ_ASSERT(aKeyframes, "The keyframe array should be valid"); + MOZ_ASSERT(aTimingFunction, "The timing function should be valid"); + MOZ_ASSERT(aOffset >= 0. && aOffset <= 1., + "The offset should be in the range of [0.0, 1.0]"); + + size_t keyframeIndex; + switch (aSearchDirection) { + case KeyframeSearchDirection::Forwards: + if (nsAnimationManager::FindMatchingKeyframe( + *aKeyframes, aOffset, *aTimingFunction, aComposition, + keyframeIndex)) { + return &(*aKeyframes)[keyframeIndex]; + } + break; + case KeyframeSearchDirection::Backwards: + if (nsAnimationManager::FindMatchingKeyframe( + Reversed(*aKeyframes), aOffset, *aTimingFunction, aComposition, + keyframeIndex)) { + return &(*aKeyframes)[aKeyframes->Length() - 1 - keyframeIndex]; + } + keyframeIndex = aKeyframes->Length() - 1; + break; + } + + Keyframe* keyframe = aKeyframes->InsertElementAt( + aInsertPosition == KeyframeInsertPosition::Prepend ? 0 : keyframeIndex); + keyframe->mOffset.emplace(aOffset); + if (!aTimingFunction->IsLinearKeyword()) { + keyframe->mTimingFunction.emplace(*aTimingFunction); + } + keyframe->mComposite = aComposition; + + return keyframe; +} + +Keyframe* Gecko_GetOrCreateKeyframeAtStart( + nsTArray* aKeyframes, float aOffset, + const StyleComputedTimingFunction* aTimingFunction, + const CompositeOperationOrAuto aComposition) { + MOZ_ASSERT(aKeyframes->IsEmpty() || + aKeyframes->ElementAt(0).mOffset.value() >= aOffset, + "The offset should be less than or equal to the first keyframe's " + "offset if there are exisiting keyframes"); + + return GetOrCreateKeyframe(aKeyframes, aOffset, aTimingFunction, aComposition, + KeyframeSearchDirection::Forwards, + KeyframeInsertPosition::Prepend); +} + +Keyframe* Gecko_GetOrCreateInitialKeyframe( + nsTArray* aKeyframes, + const StyleComputedTimingFunction* aTimingFunction, + const CompositeOperationOrAuto aComposition) { + return GetOrCreateKeyframe(aKeyframes, 0., aTimingFunction, aComposition, + KeyframeSearchDirection::Forwards, + KeyframeInsertPosition::LastForOffset); +} + +Keyframe* Gecko_GetOrCreateFinalKeyframe( + nsTArray* aKeyframes, + const StyleComputedTimingFunction* aTimingFunction, + const CompositeOperationOrAuto aComposition) { + return GetOrCreateKeyframe(aKeyframes, 1., aTimingFunction, aComposition, + KeyframeSearchDirection::Backwards, + KeyframeInsertPosition::LastForOffset); +} + +PropertyValuePair* Gecko_AppendPropertyValuePair( + nsTArray* aProperties, + const mozilla::AnimatedPropertyID* aProperty) { + MOZ_ASSERT(aProperties); + MOZ_ASSERT( + aProperty->IsCustom() || + !nsCSSProps::PropHasFlags(aProperty->mID, CSSPropFlags::IsLogical)); + return aProperties->AppendElement(PropertyValuePair{*aProperty}); +} + +void Gecko_GetComputedURLSpec(const StyleComputedUrl* aURL, nsCString* aOut) { + MOZ_ASSERT(aURL); + MOZ_ASSERT(aOut); + if (aURL->IsLocalRef()) { + aOut->Assign(aURL->SpecifiedSerialization()); + return; + } + Gecko_GetComputedImageURLSpec(aURL, aOut); +} + +void Gecko_GetComputedImageURLSpec(const StyleComputedUrl* aURL, + nsCString* aOut) { + if (aURL->IsLocalRef() && + StaticPrefs::layout_css_computed_style_dont_resolve_image_local_refs()) { + aOut->Assign(aURL->SpecifiedSerialization()); + return; + } + + if (nsIURI* uri = aURL->GetURI()) { + nsresult rv = uri->GetSpec(*aOut); + if (NS_SUCCEEDED(rv)) { + return; + } + } + + // Empty URL computes to empty, per spec: + if (aURL->SpecifiedSerialization().IsEmpty()) { + aOut->Truncate(); + } else { + aOut->AssignLiteral("about:invalid"); + } +} + +bool Gecko_IsSupportedImageMimeType(const uint8_t* aMimeType, + const uint32_t aLen) { + nsDependentCSubstring mime(reinterpret_cast(aMimeType), aLen); + return imgLoader::SupportImageWithMimeType( + mime, AcceptedMimeTypes::IMAGES_AND_DOCUMENTS); +} + +void Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut) { + // TODO(emilio): Do we have more useful stuff to put here, maybe? + if (aURI) { + *aOut = aURI->GetSpecOrDefault(); + } +} + +// XXX Implemented by hand because even though it's thread-safe, only the +// subclasses have the HasThreadSafeRefCnt bits. +void Gecko_AddRefnsIURIArbitraryThread(nsIURI* aPtr) { NS_ADDREF(aPtr); } +void Gecko_ReleasensIURIArbitraryThread(nsIURI* aPtr) { NS_RELEASE(aPtr); } + +void Gecko_nsIReferrerInfo_Debug(nsIReferrerInfo* aReferrerInfo, + nsCString* aOut) { + if (aReferrerInfo) { + if (nsCOMPtr referrer = aReferrerInfo->GetComputedReferrer()) { + *aOut = referrer->GetSpecOrDefault(); + } + } +} + +template +void DebugListAttributes(const ElementLike& aElement, nsCString& aOut) { + const uint32_t kMaxAttributeLength = 40; + + uint32_t i = 0; + while (BorrowedAttrInfo info = aElement.GetAttrInfoAt(i++)) { + aOut.AppendLiteral(" "); + if (nsAtom* prefix = info.mName->GetPrefix()) { + aOut.Append(NS_ConvertUTF16toUTF8(nsDependentAtomString(prefix))); + aOut.AppendLiteral(":"); + } + aOut.Append( + NS_ConvertUTF16toUTF8(nsDependentAtomString(info.mName->LocalName()))); + if (!info.mValue) { + continue; + } + aOut.AppendLiteral("=\""); + nsAutoString value; + info.mValue->ToString(value); + if (value.Length() > kMaxAttributeLength) { + value.Truncate(kMaxAttributeLength - 3); + value.AppendLiteral("..."); + } + aOut.Append(NS_ConvertUTF16toUTF8(value)); + aOut.AppendLiteral("\""); + } +} + +void Gecko_Element_DebugListAttributes(const Element* aElement, + nsCString* aOut) { + DebugListAttributes(*aElement, *aOut); +} + +void Gecko_Snapshot_DebugListAttributes(const ServoElementSnapshot* aSnapshot, + nsCString* aOut) { + DebugListAttributes(*aSnapshot, *aOut); +} + +NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData); + +void Gecko_nsStyleFont_SetLang(nsStyleFont* aFont, nsAtom* aAtom) { + aFont->mLanguage = dont_AddRef(aAtom); + aFont->mExplicitLanguage = true; +} + +void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, + const nsStyleFont* aSource) { + aFont->mLanguage = aSource->mLanguage; +} + +Length Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont* aFont, + const Document* aDocument) { + // Don't change font-size:0, since that would un-hide hidden text. + if (aFont->mSize.IsZero()) { + return {0}; + } + // Don't change it for docs where we don't enable the min-font-size. + if (!aFont->MinFontSizeEnabled()) { + return {0}; + } + Length minFontSize; + bool needsCache = false; + + auto MinFontSize = [&](bool* aNeedsToCache) { + const auto* prefs = + aDocument->GetFontPrefsForLang(aFont->mLanguage, aNeedsToCache); + return prefs ? prefs->mMinimumFontSize : Length{0}; + }; + + { + AutoReadLock guard(*sServoFFILock); + minFontSize = MinFontSize(&needsCache); + } + + if (needsCache) { + AutoWriteLock guard(*sServoFFILock); + minFontSize = MinFontSize(nullptr); + } + + if (minFontSize.ToCSSPixels() <= 0.0f) { + return {0}; + } + + minFontSize.ScaleBy(aFont->mMinFontSizeRatio); + minFontSize.ScaleBy(1.0f / 100.0f); + return minFontSize; +} + +static StaticRefPtr gUACacheReporter; + +namespace mozilla { + +void InitializeServo() { + URLExtraData::Init(); + Servo_Initialize(URLExtraData::Dummy(), URLExtraData::DummyChrome()); + + gUACacheReporter = new UACacheReporter(); + RegisterWeakMemoryReporter(gUACacheReporter); + + sServoFFILock = new RWLock("Servo::FFILock"); +} + +void ShutdownServo() { + MOZ_ASSERT(sServoFFILock); + + UnregisterWeakMemoryReporter(gUACacheReporter); + gUACacheReporter = nullptr; + + sServoFFILock = nullptr; + Servo_Shutdown(); + + URLExtraData::Shutdown(); +} + +void AssertIsMainThreadOrServoFontMetricsLocked() { + if (!NS_IsMainThread()) { + MOZ_ASSERT(sServoFFILock && + sServoFFILock->LockedForWritingByCurrentThread()); + } +} + +} // namespace mozilla + +GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext* aPresContext, + bool aIsVertical, + const nsStyleFont* aFont, + Length aFontSize, bool aUseUserFontSet, + bool aRetrieveMathScales) { + AutoWriteLock guard(*sServoFFILock); + + // Getting font metrics can require some main thread only work to be + // done, such as work that needs to touch non-threadsafe refcounted + // objects (like the DOM FontFace/FontFaceSet objects), network loads, etc. + // + // To handle this work, font code checks whether we are in a Servo traversal + // and if so, appends PostTraversalTasks to the current ServoStyleSet + // to be performed immediately after the traversal is finished. This + // works well for starting downloadable font loads, since we don't have + // those fonts available to get metrics for anyway. Platform fonts and + // ArrayBuffer-backed FontFace objects are handled synchronously. + + nsPresContext* presContext = const_cast(aPresContext); + RefPtr fm = nsLayoutUtils::GetMetricsFor( + presContext, aIsVertical, aFont, aFontSize, aUseUserFontSet); + auto* fontGroup = fm->GetThebesFontGroup(); + auto metrics = fontGroup->GetMetricsForCSSUnits(fm->Orientation()); + + float scriptPercentScaleDown = 0; + float scriptScriptPercentScaleDown = 0; + if (aRetrieveMathScales) { + RefPtr font = fontGroup->GetFirstValidFont(); + if (font->TryGetMathTable()) { + scriptPercentScaleDown = static_cast( + font->MathTable()->Constant(gfxMathTable::ScriptPercentScaleDown)); + scriptScriptPercentScaleDown = + static_cast(font->MathTable()->Constant( + gfxMathTable::ScriptScriptPercentScaleDown)); + } + } + + int32_t d2a = aPresContext->AppUnitsPerDevPixel(); + auto ToLength = [](nscoord aLen) { + return Length::FromPixels(CSSPixel::FromAppUnits(aLen)); + }; + return {ToLength(NS_round(metrics.xHeight * d2a)), + ToLength(NS_round(metrics.zeroWidth * d2a)), + ToLength(NS_round(metrics.capHeight * d2a)), + ToLength(NS_round(metrics.ideographicWidth * d2a)), + ToLength(NS_round(metrics.maxAscent * d2a)), + ToLength(NS_round(fontGroup->GetStyle()->size * d2a)), + scriptPercentScaleDown, + scriptScriptPercentScaleDown}; +} + +NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder); + +void Gecko_StyleSheet_FinishAsyncParse( + SheetLoadDataHolder* aData, + StyleStrong aSheetContents, + StyleUseCounters* aUseCounters) { + UniquePtr useCounters(aUseCounters); + RefPtr loadData = aData; + RefPtr sheetContents = aSheetContents.Consume(); + NS_DispatchToMainThreadQueue( + NS_NewRunnableFunction( + __func__, + [d = std::move(loadData), contents = std::move(sheetContents), + counters = std::move(useCounters)]() mutable { + MOZ_ASSERT(NS_IsMainThread()); + SheetLoadData* data = d->get(); + data->mSheet->FinishAsyncParse(contents.forget(), + std::move(counters)); + }), + EventQueuePriority::RenderBlocking); +} + +static already_AddRefed LoadImportSheet( + Loader* aLoader, StyleSheet* aParent, SheetLoadData* aParentLoadData, + LoaderReusableStyleSheets* aReusableSheets, const StyleCssUrl& aURL, + already_AddRefed aMediaList) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aLoader, "Should've catched this before"); + MOZ_ASSERT(aParent, "Only used for @import, so parent should exist!"); + + RefPtr media = new MediaList(std::move(aMediaList)); + nsCOMPtr uri = aURL.GetURI(); + nsresult rv = uri ? NS_OK : NS_ERROR_FAILURE; + + size_t previousSheetCount = aParent->ChildSheets().Length(); + if (NS_SUCCEEDED(rv)) { + // TODO(emilio): We should probably make LoadChildSheet return the + // stylesheet rather than the return code. + rv = aLoader->LoadChildSheet(*aParent, aParentLoadData, uri, media, + aReusableSheets); + } + + if (NS_FAILED(rv) || previousSheetCount == aParent->ChildSheets().Length()) { + // Servo and Gecko have different ideas of what a valid URL is, so we might + // get in here with a URL string that NS_NewURI can't handle. We may also + // reach here via an import cycle. For the import cycle case, we need some + // sheet object per spec, even if its empty. DevTools uses the URI to + // realize it has hit an import cycle, so we mark it complete to make the + // sheet readable from JS. + RefPtr emptySheet = + aParent->CreateEmptyChildSheet(media.forget()); + // Make a dummy URI if we don't have one because some methods assume + // non-null URIs. + if (!uri) { + NS_NewURI(getter_AddRefs(uri), "about:invalid"_ns); + } + emptySheet->SetURIs(uri, uri, uri); + emptySheet->SetPrincipal(aURL.ExtraData().Principal()); + nsCOMPtr referrerInfo = + ReferrerInfo::CreateForExternalCSSResources(emptySheet); + emptySheet->SetReferrerInfo(referrerInfo); + emptySheet->SetComplete(); + aParent->AppendStyleSheet(*emptySheet); + return emptySheet.forget(); + } + + RefPtr sheet = aParent->ChildSheets().LastElement(); + return sheet.forget(); +} + +StyleSheet* Gecko_LoadStyleSheet(Loader* aLoader, StyleSheet* aParent, + SheetLoadData* aParentLoadData, + LoaderReusableStyleSheets* aReusableSheets, + const StyleCssUrl* aUrl, + StyleStrong aMediaList) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aUrl); + + return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets, + *aUrl, aMediaList.Consume()) + .take(); +} + +void Gecko_LoadStyleSheetAsync(SheetLoadDataHolder* aParentData, + const StyleCssUrl* aUrl, + StyleStrong aMediaList, + StyleStrong aImportRule) { + MOZ_ASSERT(aUrl); + RefPtr loadData = aParentData; + RefPtr mediaList = aMediaList.Consume(); + RefPtr importRule = aImportRule.Consume(); + NS_DispatchToMainThreadQueue( + NS_NewRunnableFunction( + __func__, + [data = std::move(loadData), url = StyleCssUrl(*aUrl), + media = std::move(mediaList), + import = std::move(importRule)]() mutable { + MOZ_ASSERT(NS_IsMainThread()); + SheetLoadData* d = data->get(); + RefPtr sheet = LoadImportSheet( + d->mLoader, d->mSheet, d, nullptr, url, media.forget()); + Servo_ImportRule_SetSheet(import, sheet); + }), + EventQueuePriority::RenderBlocking); +} + +void Gecko_AddPropertyToSet(nsCSSPropertyIDSet* aPropertySet, + nsCSSPropertyID aProperty) { + aPropertySet->AddProperty(aProperty); +} + +bool Gecko_DocumentRule_UseForPresentation( + const Document* aDocument, const nsACString* aPattern, + DocumentMatchingFunction aMatchingFunction) { + MOZ_ASSERT(NS_IsMainThread()); + + nsIURI* docURI = aDocument->GetDocumentURI(); + nsAutoCString docURISpec; + if (docURI) { + // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules. + nsresult rv = docURI->GetSpec(docURISpec); + NS_ENSURE_SUCCESS(rv, false); + } + + return CSSMozDocumentRule::Match(aDocument, docURI, docURISpec, *aPattern, + aMatchingFunction); +} + +void Gecko_SetJemallocThreadLocalArena(bool enabled) { +#if defined(MOZ_MEMORY) + jemalloc_thread_local_arena(enabled); +#endif +} + +template +void Construct(T* aPtr, const Document* aDoc) { + if constexpr (std::is_constructible_v) { + MOZ_ASSERT(aDoc); + new (KnownNotNull, aPtr) T(*aDoc); + } else { + MOZ_ASSERT(!aDoc); + new (KnownNotNull, aPtr) T(); + // These instance are intentionally global, and we don't want leakcheckers + // to report them. + aPtr->MarkLeaked(); + } +} + +#define STYLE_STRUCT(name) \ + void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \ + const Document* doc) { \ + Construct(ptr, doc); \ + } \ + void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \ + const nsStyle##name* other) { \ + new (ptr) nsStyle##name(*other); \ + } \ + void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr) { \ + ptr->~nsStyle##name(); \ + } + +#include "nsStyleStructList.h" + +#undef STYLE_STRUCT + +bool Gecko_ErrorReportingEnabled(const StyleSheet* aSheet, + const Loader* aLoader, + uint64_t* aOutWindowId) { + if (!ErrorReporter::ShouldReportErrors(aSheet, aLoader)) { + return false; + } + *aOutWindowId = ErrorReporter::FindInnerWindowId(aSheet, aLoader); + return true; +} + +void Gecko_ReportUnexpectedCSSError( + const uint64_t aWindowId, nsIURI* aURI, const char* message, + const char* param, uint32_t paramLen, const char* prefix, + const char* prefixParam, uint32_t prefixParamLen, const char* suffix, + const char* source, uint32_t sourceLen, const char* selectors, + uint32_t selectorsLen, uint32_t lineNumber, uint32_t colNumber) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + ErrorReporter reporter(aWindowId); + + if (prefix) { + if (prefixParam) { + nsDependentCSubstring paramValue(prefixParam, prefixParamLen); + AutoTArray wideParam; + CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); + reporter.ReportUnexpectedUnescaped(prefix, wideParam); + } else { + reporter.ReportUnexpected(prefix); + } + } + + if (param) { + nsDependentCSubstring paramValue(param, paramLen); + AutoTArray wideParam; + CopyUTF8toUTF16(paramValue, *wideParam.AppendElement()); + reporter.ReportUnexpectedUnescaped(message, wideParam); + } else { + reporter.ReportUnexpected(message); + } + + if (suffix) { + reporter.ReportUnexpected(suffix); + } + nsDependentCSubstring sourceValue(source, sourceLen); + nsDependentCSubstring selectorsValue(selectors, selectorsLen); + reporter.OutputError(sourceValue, selectorsValue, lineNumber + 1, colNumber, + aURI); +} + +void Gecko_ContentList_AppendAll(nsSimpleContentList* aList, + const Element** aElements, size_t aLength) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aElements); + MOZ_ASSERT(aLength); + MOZ_ASSERT(aList); + + aList->SetCapacity(aLength); + + for (size_t i = 0; i < aLength; ++i) { + aList->AppendElement(const_cast(aElements[i])); + } +} + +const nsTArray* Gecko_Document_GetElementsWithId(const Document* aDoc, + nsAtom* aId) { + MOZ_ASSERT(aDoc); + MOZ_ASSERT(aId); + return aDoc->GetAllElementsForId(aId); +} + +const nsTArray* Gecko_ShadowRoot_GetElementsWithId( + const ShadowRoot* aShadowRoot, nsAtom* aId) { + MOZ_ASSERT(aShadowRoot); + MOZ_ASSERT(aId); + return aShadowRoot->GetAllElementsForId(aId); +} + +bool Gecko_ComputeBoolPrefMediaQuery(nsAtom* aPref) { + MOZ_ASSERT(NS_IsMainThread()); + // This map leaks until shutdown, but that's fine, all the values are + // controlled by us so it's not expected to be big. + static StaticAutoPtr, bool>> sRegisteredPrefs; + if (!sRegisteredPrefs) { + if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) { + // Styling doesn't really matter much at this point, don't bother. + return false; + } + sRegisteredPrefs = new nsTHashMap, bool>(); + ClearOnShutdown(&sRegisteredPrefs); + } + return sRegisteredPrefs->LookupOrInsertWith(aPref, [&] { + nsAutoAtomCString prefName(aPref); + Preferences::RegisterCallback( + [](const char* aPrefName, void*) { + if (sRegisteredPrefs) { + RefPtr name = NS_Atomize(nsDependentCString(aPrefName)); + sRegisteredPrefs->InsertOrUpdate(name, + Preferences::GetBool(aPrefName)); + } + LookAndFeel::NotifyChangedAllWindows( + widget::ThemeChangeKind::MediaQueriesOnly); + }, + prefName); + return Preferences::GetBool(prefName.get()); + }); +} + +bool Gecko_IsFontFormatSupported(StyleFontFaceSourceFormatKeyword aFormat) { + return gfxPlatform::GetPlatform()->IsFontFormatSupported( + aFormat, StyleFontFaceSourceTechFlags::Empty()); +} + +bool Gecko_IsFontTechSupported(StyleFontFaceSourceTechFlags aFlag) { + return gfxPlatform::GetPlatform()->IsFontFormatSupported( + StyleFontFaceSourceFormatKeyword::None, aFlag); +} + +bool Gecko_IsKnownIconFontFamily(const nsAtom* aFamilyName) { + return gfxPlatform::GetPlatform()->IsKnownIconFontFamily(aFamilyName); +} + +bool Gecko_IsInServoTraversal() { return ServoStyleSet::IsInServoTraversal(); } + +bool Gecko_IsMainThread() { return NS_IsMainThread(); } + +bool Gecko_IsDOMWorkerThread() { return !!GetCurrentThreadWorkerPrivate(); } + +int32_t Gecko_GetNumStyleThreads() { + if (const auto& cpuInfo = hal::GetHeterogeneousCpuInfo()) { + size_t numBigCpus = cpuInfo->mBigCpus.Count(); + // If CPUs are homogeneous we do not need to override stylo's + // default number of threads. + if (numBigCpus != cpuInfo->mTotalNumCpus) { + // From testing on a variety of devices it appears using only + // the number of big cores gives best performance when there are + // 2 or more big cores. If there are fewer than 2 big cores then + // additionally using the medium cores performs better. + if (numBigCpus >= 2) { + return static_cast(numBigCpus); + } + return static_cast(numBigCpus + cpuInfo->mMediumCpus.Count()); + } + } + + return -1; +} + +const nsAttrValue* Gecko_GetSVGAnimatedClass(const Element* aElement) { + MOZ_ASSERT(aElement->IsSVGElement()); + return static_cast(aElement)->GetAnimatedClassName(); +} + +bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue* aValue) { + MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom || + aValue->Type() == nsAttrValue::eString || + aValue->Type() == nsAttrValue::eAtomArray); + MOZ_ASSERT_IF( + aValue->Type() == nsAttrValue::eString, + nsContentUtils::TrimWhitespace( + aValue->GetStringValue()) + .IsEmpty()); + return true; +} + +void Gecko_GetSafeAreaInsets(const nsPresContext* aPresContext, float* aTop, + float* aRight, float* aBottom, float* aLeft) { + MOZ_ASSERT(aPresContext); + ScreenIntMargin safeAreaInsets = aPresContext->GetSafeAreaInsets(); + *aTop = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.top); + *aRight = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.right); + *aBottom = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.bottom); + *aLeft = aPresContext->DevPixelsToFloatCSSPixels(safeAreaInsets.left); +} + +void Gecko_PrintfStderr(const nsCString* aStr) { + printf_stderr("%s", aStr->get()); +} + +nsAtom* Gecko_Element_ImportedPart(const nsAttrValue* aValue, + nsAtom* aPartName) { + if (aValue->Type() != nsAttrValue::eShadowParts) { + return nullptr; + } + return aValue->GetShadowPartsValue().GetReverse(aPartName); +} + +nsAtom** Gecko_Element_ExportedParts(const nsAttrValue* aValue, + nsAtom* aPartName, size_t* aOutLength) { + if (aValue->Type() != nsAttrValue::eShadowParts) { + return nullptr; + } + auto* parts = aValue->GetShadowPartsValue().Get(aPartName); + if (!parts) { + return nullptr; + } + *aOutLength = parts->Length(); + static_assert(sizeof(RefPtr) == sizeof(nsAtom*)); + static_assert(alignof(RefPtr) == alignof(nsAtom*)); + return reinterpret_cast(parts->Elements()); +} + +bool StyleSingleFontFamily::IsNamedFamily(const nsAString& aFamilyName) const { + if (!IsFamilyName()) { + return false; + } + nsDependentAtomString name(AsFamilyName().name.AsAtom()); + return name.Equals(aFamilyName, nsCaseInsensitiveStringComparator); +} + +StyleSingleFontFamily StyleSingleFontFamily::Parse( + const nsACString& aFamilyOrGenericName) { + // should only be passed a single font - not entirely correct, a family + // *could* have a comma in it but in practice never does so + // for debug purposes this is fine + NS_ASSERTION(aFamilyOrGenericName.FindChar(',') == -1, + "Convert method should only be passed a single family name"); + + auto genericType = Servo_GenericFontFamily_Parse(&aFamilyOrGenericName); + if (genericType != StyleGenericFontFamily::None) { + return Generic(genericType); + } + return FamilyName({StyleAtom(NS_Atomize(aFamilyOrGenericName)), + StyleFontFamilyNameSyntax::Identifiers}); +} + +void StyleSingleFontFamily::AppendToString(nsACString& aName, + bool aQuote) const { + if (IsFamilyName()) { + const auto& name = AsFamilyName(); + if (!aQuote) { + aName.Append(nsAutoAtomCString(name.name.AsAtom())); + return; + } + Servo_FamilyName_Serialize(&name, &aName); + return; + } + + switch (AsGeneric()) { + case StyleGenericFontFamily::None: + case StyleGenericFontFamily::MozEmoji: + MOZ_FALLTHROUGH_ASSERT("Should never appear in a font-family name!"); + case StyleGenericFontFamily::Serif: + return aName.AppendLiteral("serif"); + case StyleGenericFontFamily::SansSerif: + return aName.AppendLiteral("sans-serif"); + case StyleGenericFontFamily::Monospace: + return aName.AppendLiteral("monospace"); + case StyleGenericFontFamily::Cursive: + return aName.AppendLiteral("cursive"); + case StyleGenericFontFamily::Fantasy: + return aName.AppendLiteral("fantasy"); + case StyleGenericFontFamily::SystemUi: + return aName.AppendLiteral("system-ui"); + } + MOZ_ASSERT_UNREACHABLE("Unknown generic font-family!"); + return aName.AppendLiteral("serif"); +} + +StyleFontFamilyList StyleFontFamilyList::WithNames( + nsTArray&& aNames) { + StyleFontFamilyList list; + Servo_FontFamilyList_WithNames(&aNames, &list); + return list; +} + +StyleFontFamilyList StyleFontFamilyList::WithOneUnquotedFamily( + const nsACString& aName) { + AutoTArray names; + names.AppendElement(StyleSingleFontFamily::FamilyName( + {StyleAtom(NS_Atomize(aName)), StyleFontFamilyNameSyntax::Identifiers})); + return WithNames(std::move(names)); +} diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h new file mode 100644 index 0000000000..7bb839ae18 --- /dev/null +++ b/layout/style/GeckoBindings.h @@ -0,0 +1,660 @@ +/* -*- 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/. */ + +/* FFI functions for Servo to call into Gecko */ + +#ifndef mozilla_GeckoBindings_h +#define mozilla_GeckoBindings_h + +#include + +#include "mozilla/ServoTypes.h" +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/css/DocumentMatchingFunction.h" +#include "mozilla/css/SheetLoadData.h" +#include "mozilla/dom/Document.h" +#include "mozilla/EffectCompositor.h" +#include "mozilla/PreferenceSheet.h" +#include "nsStyleStruct.h" +#include "COLRFonts.h" + +class nsAtom; +class nsIURI; +class nsSimpleContentList; +struct nsFont; +class ServoComputedData; + +namespace mozilla { +class ComputedStyle; +class SeenPtrs; +class ServoElementSnapshot; +class ServoElementSnapshotTable; +class StyleSheet; +enum class PseudoStyleType : uint8_t; +enum class PointerCapabilities : uint8_t; +enum class UpdateAnimationsTasks : uint8_t; +struct Keyframe; +struct StyleStylesheetContents; + +namespace css { +class LoaderReusableStyleSheets; +} +namespace dom { +enum class CompositeOperationOrAuto : uint8_t; +enum class ScreenColorGamut : uint8_t; +} // namespace dom +} // namespace mozilla + +#ifdef NIGHTLY_BUILD +const bool GECKO_IS_NIGHTLY = true; +#else +const bool GECKO_IS_NIGHTLY = false; +#endif + +#define NS_DECL_THREADSAFE_FFI_REFCOUNTING(class_, name_) \ + void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr); \ + void Gecko_Release##name_##ArbitraryThread(class_* aPtr); +#define NS_IMPL_THREADSAFE_FFI_REFCOUNTING(class_, name_) \ + static_assert(class_::HasThreadSafeRefCnt::value, \ + "NS_DECL_THREADSAFE_FFI_REFCOUNTING can only be used with " \ + "classes that have thread-safe refcounting"); \ + void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr) { NS_ADDREF(aPtr); } \ + void Gecko_Release##name_##ArbitraryThread(class_* aPtr) { NS_RELEASE(aPtr); } + +extern "C" { + +NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsIURI, nsIURI); + +// Debugging stuff. +void Gecko_Element_DebugListAttributes(const mozilla::dom::Element*, + nsCString*); + +void Gecko_Snapshot_DebugListAttributes(const mozilla::ServoElementSnapshot*, + nsCString*); + +bool Gecko_IsSignificantChild(const nsINode*, bool whitespace_is_significant); + +const nsINode* Gecko_GetLastChild(const nsINode*); +const nsINode* Gecko_GetFlattenedTreeParentNode(const nsINode*); +const mozilla::dom::Element* Gecko_GetBeforeOrAfterPseudo( + const mozilla::dom::Element*, bool is_before); +const mozilla::dom::Element* Gecko_GetMarkerPseudo( + const mozilla::dom::Element*); + +nsTArray* Gecko_GetAnonymousContentForElement( + const mozilla::dom::Element*); +void Gecko_DestroyAnonymousContentList(nsTArray* anon_content); + +const nsTArray>* Gecko_GetAssignedNodes( + const mozilla::dom::Element*); + +void Gecko_GetQueryContainerSize(const mozilla::dom::Element*, + nscoord* aOutWidth, nscoord* aOutHeight); + +void Gecko_ComputedStyle_Init(mozilla::ComputedStyle* context, + const ServoComputedData* values, + mozilla::PseudoStyleType pseudo_type); + +void Gecko_ComputedStyle_Destroy(mozilla::ComputedStyle* context); + +// By default, Servo walks the DOM by traversing the siblings of the DOM-view +// first child. This generally works, but misses anonymous children, which we +// want to traverse during styling. To support these cases, we create an +// optional stack-allocated iterator in aIterator for nodes that need it. +void Gecko_ConstructStyleChildrenIterator(const mozilla::dom::Element*, + mozilla::dom::StyleChildrenIterator*); + +void Gecko_DestroyStyleChildrenIterator(mozilla::dom::StyleChildrenIterator*); + +const nsINode* Gecko_GetNextStyleChild(mozilla::dom::StyleChildrenIterator*); + +nsAtom* Gecko_Element_ImportedPart(const nsAttrValue*, nsAtom*); +nsAtom** Gecko_Element_ExportedParts(const nsAttrValue*, nsAtom*, + size_t* aOutLength); + +NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::SheetLoadDataHolder, + SheetLoadDataHolder); + +void Gecko_StyleSheet_FinishAsyncParse( + mozilla::css::SheetLoadDataHolder* data, + mozilla::StyleStrong sheet_contents, + mozilla::StyleUseCounters* use_counters); + +mozilla::StyleSheet* Gecko_LoadStyleSheet( + mozilla::css::Loader* loader, mozilla::StyleSheet* parent, + mozilla::css::SheetLoadData* parent_load_data, + mozilla::css::LoaderReusableStyleSheets* reusable_sheets, + const mozilla::StyleCssUrl* url, + mozilla::StyleStrong media_list); + +void Gecko_LoadStyleSheetAsync( + mozilla::css::SheetLoadDataHolder* parent_data, + const mozilla::StyleCssUrl* url, + mozilla::StyleStrong, + mozilla::StyleStrong); + +// Selector Matching. +uint64_t Gecko_ElementState(const mozilla::dom::Element*); +bool Gecko_IsRootElement(const mozilla::dom::Element*); + +bool Gecko_MatchLang(const mozilla::dom::Element*, nsAtom* override_lang, + bool has_override_lang, const char16_t* value); + +nsAtom* Gecko_GetXMLLangValue(const mozilla::dom::Element*); + +const mozilla::PreferenceSheet::Prefs* Gecko_GetPrefSheetPrefs( + const mozilla::dom::Document*); + +bool Gecko_IsTableBorderNonzero(const mozilla::dom::Element* element); +bool Gecko_IsSelectListBox(const mozilla::dom::Element* element); + +// Attributes. +#define SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \ + nsAtom* prefix_##LangValue(implementor_ element); + +bool Gecko_AttrEquals(const nsAttrValue*, const nsAtom*, bool aIgnoreCase); +bool Gecko_AttrDashEquals(const nsAttrValue*, const nsAtom*, bool aIgnoreCase); +bool Gecko_AttrIncludes(const nsAttrValue*, const nsAtom*, bool aIgnoreCase); +bool Gecko_AttrHasSubstring(const nsAttrValue*, const nsAtom*, + bool aIgnoreCase); +bool Gecko_AttrHasPrefix(const nsAttrValue*, const nsAtom*, bool aIgnoreCase); +bool Gecko_AttrHasSuffix(const nsAttrValue*, const nsAtom*, bool aIgnoreCase); + +bool Gecko_AssertClassAttrValueIsSane(const nsAttrValue*); +const nsAttrValue* Gecko_GetSVGAnimatedClass(const mozilla::dom::Element*); + +SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, + const mozilla::dom::Element*) + +SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS( + Gecko_Snapshot, const mozilla::ServoElementSnapshot*) + +#undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS + +// Style attributes. +const mozilla::StyleLockedDeclarationBlock* Gecko_GetStyleAttrDeclarationBlock( + const mozilla::dom::Element* element); + +void Gecko_UnsetDirtyStyleAttr(const mozilla::dom::Element* element); + +const mozilla::StyleLockedDeclarationBlock* +Gecko_GetHTMLPresentationAttrDeclarationBlock( + const mozilla::dom::Element* element); + +const mozilla::StyleLockedDeclarationBlock* +Gecko_GetExtraContentStyleDeclarations(const mozilla::dom::Element* element); + +const mozilla::StyleLockedDeclarationBlock* +Gecko_GetUnvisitedLinkAttrDeclarationBlock( + const mozilla::dom::Element* element); + +const mozilla::StyleLockedDeclarationBlock* +Gecko_GetVisitedLinkAttrDeclarationBlock(const mozilla::dom::Element* element); + +const mozilla::StyleLockedDeclarationBlock* +Gecko_GetActiveLinkAttrDeclarationBlock(const mozilla::dom::Element* element); + +// Visited handling. + +// Returns whether visited styles are enabled for a given document. +bool Gecko_VisitedStylesEnabled(const mozilla::dom::Document*); + +// Animations +bool Gecko_GetAnimationRule( + const mozilla::dom::Element* aElementOrPseudo, + mozilla::EffectCompositor::CascadeLevel aCascadeLevel, + mozilla::StyleAnimationValueMap* aAnimationValues); + +bool Gecko_StyleAnimationsEquals( + const nsStyleAutoArray*, + const nsStyleAutoArray*); + +bool Gecko_StyleScrollTimelinesEquals( + const nsStyleAutoArray*, + const nsStyleAutoArray*); + +bool Gecko_StyleViewTimelinesEquals( + const nsStyleAutoArray*, + const nsStyleAutoArray*); + +void Gecko_UpdateAnimations(const mozilla::dom::Element* aElementOrPseudo, + const mozilla::ComputedStyle* aOldComputedValues, + const mozilla::ComputedStyle* aComputedValues, + mozilla::UpdateAnimationsTasks aTasks); + +size_t Gecko_GetAnimationEffectCount( + const mozilla::dom::Element* aElementOrPseudo); +bool Gecko_ElementHasAnimations(const mozilla::dom::Element* aElementOrPseudo); +bool Gecko_ElementHasCSSAnimations( + const mozilla::dom::Element* aElementOrPseudo); +bool Gecko_ElementHasCSSTransitions( + const mozilla::dom::Element* aElementOrPseudo); +bool Gecko_ElementHasWebAnimations( + const mozilla::dom::Element* aElementOrPseudo); +size_t Gecko_ElementTransitions_Length( + const mozilla::dom::Element* aElementOrPseudo); + +nsCSSPropertyID Gecko_ElementTransitions_PropertyAt( + const mozilla::dom::Element* aElementOrPseudo, size_t aIndex); + +const mozilla::StyleAnimationValue* Gecko_ElementTransitions_EndValueAt( + const mozilla::dom::Element* aElementOrPseudo, size_t aIndex); + +double Gecko_GetProgressFromComputedTiming(const mozilla::ComputedTiming*); + +double Gecko_GetPositionInSegment(const mozilla::AnimationPropertySegment*, + double aProgress, bool aBeforeFlag); + +// Get servo's AnimationValue for |aProperty| from the cached base style +// |aBaseStyles|. +// |aBaseStyles| is nsRefPtrHashtable, +// StyleAnimationValue>. +// We use RawServoAnimationValueTableBorrowed to avoid exposing +// nsRefPtrHashtable in FFI. +const mozilla::StyleAnimationValue* Gecko_AnimationGetBaseStyle( + const RawServoAnimationValueTable* aBaseStyles, + const mozilla::AnimatedPropertyID* aProperty); + +void Gecko_StyleTransition_SetUnsupportedProperty( + mozilla::StyleTransition* aTransition, nsAtom* aAtom); + +// Atoms. +nsAtom* Gecko_Atomize(const char* aString, uint32_t aLength); +nsAtom* Gecko_Atomize16(const nsAString* aString); +void Gecko_AddRefAtom(nsAtom* aAtom); +void Gecko_ReleaseAtom(nsAtom* aAtom); + +// will not run destructors on dst, give it uninitialized memory +// font_id is LookAndFeel::FontID +void Gecko_nsFont_InitSystem(nsFont* dst, mozilla::StyleSystemFont font_id, + const nsStyleFont* font, + const mozilla::dom::Document*); + +void Gecko_nsFont_Destroy(nsFont* dst); + +// The gfxFontFeatureValueSet returned from this function has zero reference. +gfxFontFeatureValueSet* Gecko_ConstructFontFeatureValueSet(); + +nsTArray* Gecko_AppendFeatureValueHashEntry( + gfxFontFeatureValueSet* value_set, nsAtom* family, uint32_t alternate, + nsAtom* name); + +// Font variant alternates +void Gecko_ClearAlternateValues(nsFont* font, size_t length); + +void Gecko_AppendAlternateValues(nsFont* font, uint32_t alternate_name, + nsAtom* atom); + +void Gecko_CopyAlternateValuesFrom(nsFont* dest, const nsFont* src); + +// The FontPaletteValueSet returned from this function has zero reference. +mozilla::gfx::FontPaletteValueSet* Gecko_ConstructFontPaletteValueSet(); + +mozilla::gfx::FontPaletteValueSet::PaletteValues* +Gecko_AppendPaletteValueHashEntry( + mozilla::gfx::FontPaletteValueSet* aPaletteValueSet, nsAtom* aFamily, + nsAtom* aName); + +void Gecko_SetFontPaletteBase( + mozilla::gfx::FontPaletteValueSet::PaletteValues* aValues, + int32_t aBasePaletteIndex); + +void Gecko_SetFontPaletteOverride( + mozilla::gfx::FontPaletteValueSet::PaletteValues* aValues, int32_t aIndex, + mozilla::StyleAbsoluteColor* aColor); + +// Visibility style +void Gecko_SetImageOrientation(nsStyleVisibility* aVisibility, + uint8_t aOrientation, bool aFlip); + +void Gecko_SetImageOrientationAsFromImage(nsStyleVisibility* aVisibility); + +void Gecko_CopyImageOrientationFrom(nsStyleVisibility* aDst, + const nsStyleVisibility* aSrc); + +// Counter style. +void Gecko_CounterStyle_ToPtr(const mozilla::StyleCounterStyle*, + mozilla::CounterStylePtr*); + +void Gecko_SetCounterStyleToNone(mozilla::CounterStylePtr*); + +void Gecko_SetCounterStyleToString(mozilla::CounterStylePtr* ptr, + const nsACString* symbol); + +void Gecko_CopyCounterStyle(mozilla::CounterStylePtr* dst, + const mozilla::CounterStylePtr* src); + +nsAtom* Gecko_CounterStyle_GetName(const mozilla::CounterStylePtr* ptr); + +const mozilla::AnonymousCounterStyle* Gecko_CounterStyle_GetAnonymous( + const mozilla::CounterStylePtr* ptr); + +// list-style-image style. +void Gecko_SetListStyleImageNone(nsStyleList* style_struct); + +void Gecko_SetListStyleImageImageValue( + nsStyleList* style_struct, const mozilla::StyleComputedImageUrl* url); + +void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src); + +// Dirtiness tracking. +void Gecko_NoteDirtyElement(const mozilla::dom::Element*); +void Gecko_NoteDirtySubtreeForInvalidation(const mozilla::dom::Element*); +void Gecko_NoteAnimationOnlyDirtyElement(const mozilla::dom::Element*); + +bool Gecko_AnimationNameMayBeReferencedFromStyle(const nsPresContext*, + nsAtom* name); + +float Gecko_GetScrollbarInlineSize(const nsPresContext*); + +// Incremental restyle. +mozilla::PseudoStyleType Gecko_GetImplementedPseudo( + const mozilla::dom::Element*); + +// We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't +// work as return values with the Linux 32-bit ABI at the moment because +// they wrap the value in a struct. +uint32_t Gecko_CalcStyleDifference(const mozilla::ComputedStyle* old_style, + const mozilla::ComputedStyle* new_style, + bool* any_style_struct_changed, + bool* reset_only_changed); + +nscoord Gecko_CalcLineHeight(const mozilla::StyleLineHeight*, + const nsPresContext*, bool aVertical, + const nsStyleFont* aAgainstFont, + const mozilla::dom::Element* aElement); + +// Get an element snapshot for a given element from the table. +const mozilla::ServoElementSnapshot* Gecko_GetElementSnapshot( + const mozilla::ServoElementSnapshotTable* table, + const mozilla::dom::Element*); + +// Have we seen this pointer before? +bool Gecko_HaveSeenPtr(mozilla::SeenPtrs* table, const void* ptr); + +// `array` must be an nsTArray +// If changing this signature, please update the +// friend function declaration in nsTArray.h +void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size); + +// Same here, `array` must be an nsTArray, for some T. +// +// Important note: Only valid for POD types, since destructors won't be run +// otherwise. This is ensured with rust traits for the relevant structs. +void Gecko_ClearPODTArray(void* array, size_t elem_size, size_t elem_align); + +void Gecko_ResizeTArrayForStrings(nsTArray* array, uint32_t length); +void Gecko_ResizeAtomArray(nsTArray>* array, uint32_t length); + +void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len, + nsStyleImageLayers::LayerType layer_type); + +void Gecko_EnsureStyleAnimationArrayLength(void* array, size_t len); +void Gecko_EnsureStyleTransitionArrayLength(void* array, size_t len); +void Gecko_EnsureStyleScrollTimelineArrayLength(void* array, size_t len); +void Gecko_EnsureStyleViewTimelineArrayLength(void* array, size_t len); + +// Searches from the beginning of |keyframes| for a Keyframe object with the +// specified offset and timing function. If none is found, a new Keyframe object +// with the specified |offset| and |timingFunction| will be prepended to +// |keyframes|. +// +// @param keyframes An array of Keyframe objects, sorted by offset. +// The first Keyframe in the array, if any, MUST have an +// offset greater than or equal to |offset|. +// @param offset The offset to search for, or, if no suitable Keyframe is +// found, the offset to use for the created Keyframe. +// Must be a floating point number in the range [0.0, 1.0]. +// @param timingFunction The timing function to match, or, if no suitable +// Keyframe is found, to set on the created Keyframe. +// @param composition The composition to match, or, if no suitable Keyframe is +// found, to set on the created Keyframe. +// +// @returns The matching or created Keyframe. +mozilla::Keyframe* Gecko_GetOrCreateKeyframeAtStart( + nsTArray* keyframes, float offset, + const mozilla::StyleComputedTimingFunction* timingFunction, + const mozilla::dom::CompositeOperationOrAuto composition); + +// As with Gecko_GetOrCreateKeyframeAtStart except that this method will search +// from the beginning of |keyframes| for a Keyframe with matching timing +// function, composition, and an offset of 0.0. +// Furthermore, if a matching Keyframe is not found, a new Keyframe will be +// inserted after the *last* Keyframe in |keyframes| with offset 0.0. +mozilla::Keyframe* Gecko_GetOrCreateInitialKeyframe( + nsTArray* keyframes, + const mozilla::StyleComputedTimingFunction* timingFunction, + const mozilla::dom::CompositeOperationOrAuto composition); + +// As with Gecko_GetOrCreateKeyframeAtStart except that this method will search +// from the *end* of |keyframes| for a Keyframe with matching timing function, +// composition, and an offset of 1.0. If a matching Keyframe is not found, a new +// Keyframe will be appended to the end of |keyframes|. +mozilla::Keyframe* Gecko_GetOrCreateFinalKeyframe( + nsTArray* keyframes, + const mozilla::StyleComputedTimingFunction* timingFunction, + const mozilla::dom::CompositeOperationOrAuto composition); + +// Appends and returns a new PropertyValuePair to |aProperties| initialized with +// its mProperty member set to |aProperty| and all other members initialized to +// their default values. +mozilla::PropertyValuePair* Gecko_AppendPropertyValuePair( + nsTArray*, + const mozilla::AnimatedPropertyID* aProperty); + +void Gecko_ResetFilters(nsStyleEffects* effects, size_t new_len); + +void Gecko_CopyFiltersFrom(nsStyleEffects* aSrc, nsStyleEffects* aDest); + +void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len); + +void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src); + +void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* svg, uint32_t len); + +void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* dst, + const nsStyleSVG* src); + +void Gecko_GetComputedURLSpec(const mozilla::StyleComputedUrl* url, + nsCString* spec); + +void Gecko_GetComputedImageURLSpec(const mozilla::StyleComputedUrl* url, + nsCString* spec); + +// Return true if the given image MIME type is supported +bool Gecko_IsSupportedImageMimeType(const uint8_t* mime_type, + const uint32_t len); + +void Gecko_nsIURI_Debug(nsIURI*, nsCString* spec); + +void Gecko_nsIReferrerInfo_Debug(nsIReferrerInfo* aReferrerInfo, + nsCString* aOut); + +NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::URLExtraData, URLExtraData); +NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsIReferrerInfo, nsIReferrerInfo); + +void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len); + +void Gecko_LoadData_Drop(mozilla::StyleLoadData*); + +void Gecko_nsStyleFont_SetLang(nsStyleFont* font, nsAtom* atom); + +void Gecko_nsStyleFont_CopyLangFrom(nsStyleFont* aFont, + const nsStyleFont* aSource); + +mozilla::Length Gecko_nsStyleFont_ComputeMinSize(const nsStyleFont*, + const mozilla::dom::Document*); + +// Computes the default generic font for a language. +mozilla::StyleGenericFontFamily +Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage( + const mozilla::dom::Document*, nsAtom* language); + +mozilla::Length Gecko_GetBaseSize(const mozilla::dom::Document*, + nsAtom* language, + mozilla::StyleGenericFontFamily); + +struct GeckoFontMetrics { + mozilla::Length mXSize; + mozilla::Length mChSize; // negatives indicate not found. + mozilla::Length mCapHeight; // negatives indicate not found. + mozilla::Length mIcWidth; // negatives indicate not found. + mozilla::Length mAscent; + mozilla::Length mComputedEmSize; + float mScriptPercentScaleDown; // zero is invalid or means not found. + float mScriptScriptPercentScaleDown; // zero is invalid or means not found. +}; + +GeckoFontMetrics Gecko_GetFontMetrics(const nsPresContext*, bool is_vertical, + const nsStyleFont* font, + mozilla::Length font_size, + bool use_user_font_set, + bool retrieve_math_scales); + +mozilla::StyleSheet* Gecko_StyleSheet_Clone( + const mozilla::StyleSheet* aSheet, + const mozilla::StyleSheet* aNewParentSheet); + +void Gecko_StyleSheet_AddRef(const mozilla::StyleSheet* aSheet); +void Gecko_StyleSheet_Release(const mozilla::StyleSheet* aSheet); +bool Gecko_IsDocumentBody(const mozilla::dom::Element* element); + +bool Gecko_IsDarkColorScheme(const mozilla::dom::Document*, + const mozilla::StyleColorScheme*); +nscolor Gecko_ComputeSystemColor(mozilla::StyleSystemColor, + const mozilla::dom::Document*, + const mozilla::StyleColorScheme*); + +// We use an int32_t here instead of a LookAndFeel::IntID/FloatID because +// forward-declaring a nested enum/struct is impossible. +int32_t Gecko_GetLookAndFeelInt(int32_t int_id); +float Gecko_GetLookAndFeelFloat(int32_t float_id); + +void Gecko_AddPropertyToSet(nsCSSPropertyIDSet*, nsCSSPropertyID); + +// Style-struct management. +#define STYLE_STRUCT(name) \ + void Gecko_Construct_Default_nsStyle##name(nsStyle##name* ptr, \ + const mozilla::dom::Document*); \ + void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \ + const nsStyle##name* other); \ + void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr); +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + +bool Gecko_DocumentRule_UseForPresentation( + const mozilla::dom::Document*, const nsACString* aPattern, + mozilla::css::DocumentMatchingFunction); + +// Allocator hinting. +void Gecko_SetJemallocThreadLocalArena(bool enabled); + +// Pseudo-element flags. +#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \ + const uint32_t SERVO_CSS_PSEUDO_ELEMENT_FLAGS_##name_ = flags_; +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT + +bool Gecko_ErrorReportingEnabled(const mozilla::StyleSheet* sheet, + const mozilla::css::Loader* loader, + uint64_t* aOutWindowId); + +void Gecko_ReportUnexpectedCSSError( + uint64_t windowId, nsIURI* uri, const char* message, const char* param, + uint32_t paramLen, const char* prefix, const char* prefixParam, + uint32_t prefixParamLen, const char* suffix, const char* source, + uint32_t sourceLen, const char* selectors, uint32_t selectorsLen, + uint32_t lineNumber, uint32_t colNumber); + +// DOM APIs. +void Gecko_ContentList_AppendAll(nsSimpleContentList* aContentList, + const mozilla::dom::Element** aElements, + size_t aLength); + +// FIXME(emilio): These two below should be a single function that takes a +// `const DocumentOrShadowRoot*`, but that doesn't make MSVC builds happy for a +// reason I haven't really dug into. +const nsTArray* Gecko_Document_GetElementsWithId( + const mozilla::dom::Document*, nsAtom* aId); + +const nsTArray* Gecko_ShadowRoot_GetElementsWithId( + const mozilla::dom::ShadowRoot*, nsAtom* aId); + +bool Gecko_ComputeBoolPrefMediaQuery(nsAtom*); + +// Check whether font format/tech is supported. +bool Gecko_IsFontFormatSupported( + mozilla::StyleFontFaceSourceFormatKeyword aFormat); +bool Gecko_IsFontTechSupported(mozilla::StyleFontFaceSourceTechFlags aFlag); + +bool Gecko_IsKnownIconFontFamily(const nsAtom* aFamilyName); + +// Returns true if we're currently performing the servo traversal. +bool Gecko_IsInServoTraversal(); + +// Returns true if we're currently on the main thread. +bool Gecko_IsMainThread(); + +// Returns true if we're currently on a DOM worker thread. +bool Gecko_IsDOMWorkerThread(); + +// Returns the preferred number of style threads to use, or -1 for no +// preference. +int32_t Gecko_GetNumStyleThreads(); + +// Media feature helpers. +// +// Defined in nsMediaFeatures.cpp. +mozilla::StyleDisplayMode Gecko_MediaFeatures_GetDisplayMode( + const mozilla::dom::Document*); + +bool Gecko_MediaFeatures_UseOverlayScrollbars(const mozilla::dom::Document*); +int32_t Gecko_MediaFeatures_GetColorDepth(const mozilla::dom::Document*); +int32_t Gecko_MediaFeatures_GetMonochromeBitsPerPixel( + const mozilla::dom::Document*); +mozilla::dom::ScreenColorGamut Gecko_MediaFeatures_ColorGamut( + const mozilla::dom::Document*); + +void Gecko_MediaFeatures_GetDeviceSize(const mozilla::dom::Document*, + nscoord* width, nscoord* height); + +float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*); +bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*); +bool Gecko_MediaFeatures_PrefersReducedTransparency( + const mozilla::dom::Document*); +mozilla::StylePrefersContrast Gecko_MediaFeatures_PrefersContrast( + const mozilla::dom::Document*); +mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme( + const mozilla::dom::Document*, bool aUseContent); +bool Gecko_MediaFeatures_InvertedColors(const mozilla::dom::Document*); +mozilla::StyleScripting Gecko_MediaFeatures_Scripting( + const mozilla::dom::Document*); + +mozilla::StyleDynamicRange Gecko_MediaFeatures_DynamicRange( + const mozilla::dom::Document*); +mozilla::StyleDynamicRange Gecko_MediaFeatures_VideoDynamicRange( + const mozilla::dom::Document*); + +mozilla::PointerCapabilities Gecko_MediaFeatures_PrimaryPointerCapabilities( + const mozilla::dom::Document*); + +mozilla::PointerCapabilities Gecko_MediaFeatures_AllPointerCapabilities( + const mozilla::dom::Document*); + +float Gecko_MediaFeatures_GetDevicePixelRatio(const mozilla::dom::Document*); + +bool Gecko_MediaFeatures_IsResourceDocument(const mozilla::dom::Document*); +bool Gecko_MediaFeatures_MatchesPlatform(mozilla::StylePlatform); +mozilla::StyleGtkThemeFamily Gecko_MediaFeatures_GtkThemeFamily(); + +void Gecko_GetSafeAreaInsets(const nsPresContext*, float*, float*, float*, + float*); + +void Gecko_PrintfStderr(const nsCString*); + +} // extern "C" + +#endif // mozilla_GeckoBindings_h diff --git a/layout/style/GenerateCSSPropertyID.py b/layout/style/GenerateCSSPropertyID.py new file mode 100644 index 0000000000..d9bdc22ec5 --- /dev/null +++ b/layout/style/GenerateCSSPropertyID.py @@ -0,0 +1,40 @@ +# 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/. + +import runpy +import string + + +def generate(output, template, dataFile): + with open(template, "r") as f: + template = string.Template(f.read()) + data = runpy.run_path(dataFile)["data"] + + longhand_count = 0 + shorthand_count = 0 + alias_count = 0 + property_ids = [] + for prop in data.values(): + if prop.type() != "alias": + if prop.type() == "longhand": + assert shorthand_count == 0 + longhand_count += 1 + else: + assert alias_count == 0 + shorthand_count += 1 + property_ids.append("eCSSProperty_{}".format(prop.id)) + else: + alias_count += 1 + property_ids.append("eCSSPropertyAlias_{}".format(prop.alias_id)) + + output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n") + output.write( + template.substitute( + { + "property_ids": "\n".join(" {},".format(p) for p in property_ids), + "longhand_count": property_ids[longhand_count], + "shorthand_count": property_ids[longhand_count + shorthand_count], + } + ) + ) diff --git a/layout/style/GenerateCSSPropsGenerated.py b/layout/style/GenerateCSSPropsGenerated.py new file mode 100644 index 0000000000..90ed792194 --- /dev/null +++ b/layout/style/GenerateCSSPropsGenerated.py @@ -0,0 +1,114 @@ +# 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/. + +import runpy +import sys +import string +import argparse + + +class PropertyWrapper(object): + __slots__ = ["index", "prop", "idlname"] + + def __init__(self, index, prop): + self.index = index + self.prop = prop + if "Internal" in prop.flags: + self.idlname = None + else: + idl_name = prop.method + if not idl_name.startswith("Moz"): + idl_name = idl_name[0].lower() + idl_name[1:] + self.idlname = idl_name + + def __getattr__(self, name): + return getattr(self.prop, name) + + +def generate(output, dataFile): + output.write( + """/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */ + +/* processed file that defines CSS property tables that can't be generated + with the pre-processor, designed to be #included in nsCSSProps.cpp */ + +""" + ) + + raw_properties = runpy.run_path(dataFile)["data"] + properties = [ + PropertyWrapper(i, p) + for i, p in enumerate(raw_properties.values()) + if p.type() != "alias" + ] + + # Generate kIDLNameTable + output.write( + "const char* const nsCSSProps::" "kIDLNameTable[eCSSProperty_COUNT] = {\n" + ) + for p in properties: + if p.idlname is None: + output.write(" nullptr, // {}\n".format(p.name)) + else: + output.write(' "{}",\n'.format(p.idlname)) + output.write("};\n\n") + + # Generate kIDLNameSortPositionTable + ps = sorted(properties, key=lambda p: p.idlname if p.idlname else "") + ps = [(p, position) for position, p in enumerate(ps)] + ps.sort(key=lambda item: item[0].index) + output.write( + "const int32_t nsCSSProps::" + "kIDLNameSortPositionTable[eCSSProperty_COUNT] = {\n" + ) + for p, position in ps: + output.write(" {},\n".format(position)) + output.write("};\n\n") + + # Generate preferences table + output.write( + "const nsCSSProps::PropertyPref " "nsCSSProps::kPropertyPrefTable[] = {\n" + ) + for p in raw_properties.values(): + if not p.pref: + continue + if p.type() != "alias": + prop_id = "eCSSProperty_" + p.id + else: + prop_id = "eCSSPropertyAlias_" + p.alias_id + output.write(' {{ {}, "{}" }},\n'.format(prop_id, p.pref)) + output.write(" { eCSSProperty_UNKNOWN, nullptr },\n") + output.write("};\n\n") + + # Generate shorthand subprop tables + names = [] + for p in properties: + if p.type() != "shorthand": + continue + name = "g{}SubpropTable".format(p.method) + names.append(name) + output.write("static const nsCSSPropertyID {}[] = {{\n".format(name)) + for subprop in p.subprops: + output.write(" eCSSProperty_{},\n".format(subprop)) + output.write(" eCSSProperty_UNKNOWN\n") + output.write("};\n\n") + output.write("const nsCSSPropertyID* const\n") + output.write( + "nsCSSProps::kSubpropertyTable[" + "eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands" + "] = {\n" + ) + for name in names: + output.write(" {},\n".format(name)) + output.write("};\n\n") + + # Generate assertions + msg = ( + "GenerateCSSPropsGenerated.py did not list properties " + "in nsCSSPropertyID order" + ) + for p in properties: + output.write( + 'static_assert(eCSSProperty_{} == {}, "{}");\n'.format(p.id, p.index, msg) + ) diff --git a/layout/style/GenerateCompositorAnimatableProperties.py b/layout/style/GenerateCompositorAnimatableProperties.py new file mode 100644 index 0000000000..b64183b679 --- /dev/null +++ b/layout/style/GenerateCompositorAnimatableProperties.py @@ -0,0 +1,35 @@ +# 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/. + +import runpy + + +def generate(output, dataFile): + output.write( + """/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */ +#ifndef COMPOSITOR_ANIMATABLE_PROPERTY_LIST +#define COMPOSITOR_ANIMATABLE_PROPERTY_LIST { \\ +""" + ) + + def can_animate_on_compositor(p): + return "CanAnimateOnCompositor" in p.flags and p.type() != "alias" + + properties = runpy.run_path(dataFile)["data"] + properties = filter(can_animate_on_compositor, properties.values()) + + count = 0 + for p in properties: + output.write(" eCSSProperty_{}, \\\n".format(p.id)) + count += 1 + + output.write("}\n") + output.write("#endif /* COMPOSITOR_ANIMATABLE_PROPERTY_LIST */\n") + + output.write("\n") + output.write("#ifndef COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH\n") + output.write( + "#define COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH {}\n".format(count) + ) + output.write("#endif /* COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH */\n") diff --git a/layout/style/GenerateComputedDOMStyleGenerated.py b/layout/style/GenerateComputedDOMStyleGenerated.py new file mode 100644 index 0000000000..2dcfabd241 --- /dev/null +++ b/layout/style/GenerateComputedDOMStyleGenerated.py @@ -0,0 +1,81 @@ +# 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/. + +import runpy + +FILE = """/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */ + +/* processed file that defines entries for nsComputedDOMStyle, designed + to be #included in nsComputedDOMStyle.cpp */ + +static constexpr size_t kEntryIndices[eCSSProperty_COUNT] = {{ + {indices} +}}; + +{asserts} + +static constexpr Entry kEntries[eCSSProperty_COUNT] = {{ + {entries} +}}; +""" + + +def generate(output, dataFile): + def order_key(p): + # Put prefixed properties after normal properties. + # The spec is unclear about this, and Blink doesn't have any sensible + # order at all, so it probably doesn't matter a lot. But originally + # Gecko put then later so we do so as well. See w3c/csswg-drafts#2827. + order = p.name.startswith("-") + return (order, p.name) + + def has_cpp_getter(p): + if not "ExposedOnGetCS" in p.flags: + return False + if "SerializedByServo" in p.flags: + return False + if p.type() == "longhand" and "IsLogical" in p.flags: + return False + return True + + def getter_entry(p): + if has_cpp_getter(p): + return "DoGet" + p.method + # Put a dummy getter here instead of nullptr because MSVC seems + # to have bug which ruins the table when we put nullptr for + # pointer-to-member-function. See bug 1471426. + return "DummyGetter" + + properties = runpy.run_path(dataFile)["data"] + + entries = [] + indices = [] + asserts = [] + index_map = {} + non_aliases = list(filter(lambda p: p.type() != "alias", properties.values())) + for i, p in enumerate(sorted(non_aliases, key=order_key)): + can_be_exposed = "true" if "ExposedOnGetCS" in p.flags else "false" + entries.append( + "{{ eCSSProperty_{}, {}, &nsComputedDOMStyle::{}}}".format( + p.id, can_be_exposed, getter_entry(p) + ) + ) + index_map[p.id] = i + i += 1 + + for i, p in enumerate(non_aliases): + indices.append(str(index_map[p.id])) + asserts.append( + 'static_assert(size_t(eCSSProperty_{}) == {}, "");'.format(p.id, i) + ) + + assert len(indices) == len(entries) + + output.write( + FILE.format( + indices=", ".join(indices), + entries=",\n ".join(entries), + asserts="\n".join(asserts), + ) + ) diff --git a/layout/style/GenerateCountedUnknownProperties.py b/layout/style/GenerateCountedUnknownProperties.py new file mode 100644 index 0000000000..b46884da6b --- /dev/null +++ b/layout/style/GenerateCountedUnknownProperties.py @@ -0,0 +1,24 @@ +# 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/. + +import re +import runpy +import string + + +def to_camel_case(ident): + return re.sub( + "(^|_|-)([a-z0-9])", lambda m: m.group(2).upper(), ident.strip("_").strip("-") + ) + + +def generate(output, prop_file): + properties = runpy.run_path(prop_file)["COUNTED_UNKNOWN_PROPERTIES"] + + output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n") + + for prop in properties: + output.write( + "COUNTED_UNKNOWN_PROPERTY({}, {})\n".format(prop, to_camel_case(prop)) + ) diff --git a/layout/style/GenerateServoCSSPropList.py b/layout/style/GenerateServoCSSPropList.py new file mode 100644 index 0000000000..0e9678ac5c --- /dev/null +++ b/layout/style/GenerateServoCSSPropList.py @@ -0,0 +1,138 @@ +# 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/. + +import buildconfig +import mozpack.path as mozpath +import os +import runpy +import subprocess +import string +import sys + +SERVO_BASE = mozpath.join(buildconfig.topsrcdir, "servo") +SERVO_PROP_BASE = mozpath.join(SERVO_BASE, "components", "style", "properties") + + +def generate_data(output, template): + output.write("# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT\n\n") + output.write( + subprocess.check_output( + [ + sys.executable, + mozpath.join(SERVO_PROP_BASE, "build.py"), + "gecko", + "geckolib", + template, + ], + universal_newlines=True, + ) + ) + + # Add all relevant files into the dependencies of the generated file. + DEP_EXTS = [".py", ".rs", ".zip"] + deps = set() + for path, dirs, files in os.walk(SERVO_PROP_BASE): + for file in files: + if os.path.splitext(file)[1] in DEP_EXTS: + deps.add(mozpath.join(path, file)) + return deps + + +def generate_header(output, data): + data = runpy.run_path(data)["data"] + + output.write( + """/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */ + +/* -*- 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/. */ + +#define CSS_PROP_DOMPROP_PREFIXED(name_) \\ + CSS_PROP_PUBLIC_OR_PRIVATE(Moz ## name_, name_) + +#ifndef CSS_PROP_LONGHAND +#define CSS_PROP_LONGHAND(name_, id_, method_, flags_, pref_) /* nothing */ +#define DEFINED_CSS_PROP_LONGHAND +#endif + +#ifndef CSS_PROP_SHORTHAND +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) /* nothing */ +#define DEFINED_CSS_PROP_SHORTHAND +#endif + +#ifndef CSS_PROP_ALIAS +#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, flags_, pref_) /* nothing */ +#define DEFINED_CSS_PROP_ALIAS +#endif + +""" + ) + + # Some flags are only used for code generation, so we don't need to + # expose them to runtime. + COMPILE_TIME_FLAGS = {"ExposedOnGetCS"} + + MACRO_NAMES = { + "longhand": "CSS_PROP_LONGHAND", + "shorthand": "CSS_PROP_SHORTHAND", + "alias": "CSS_PROP_ALIAS", + } + for prop in data.values(): + is_internal = "Internal" in prop.flags + flags = " | ".join( + "CSSPropFlags::{}".format(flag) + for flag in prop.flags + if flag not in COMPILE_TIME_FLAGS + ) + if not flags: + flags = "CSSPropFlags(0)" + pref = '"' + prop.pref + '"' + method = prop.method + if prop.type() == "alias": + params = [prop.name, prop.alias_id, prop.prop_id, method, flags, pref] + else: + if method == "CssFloat": + method = "CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float)" + elif method.startswith("Moz"): + method = "CSS_PROP_DOMPROP_PREFIXED({})".format(method[3:]) + params = [prop.name, prop.id, method, flags, pref] + excludes = [] + if is_internal: + excludes.append("CSS_PROP_LIST_EXCLUDE_INTERNAL") + if "Style" not in prop.rules: + excludes.append("CSS_PROP_LIST_EXCLUDE_NOT_IN_STYLE") + + if excludes: + output.write( + "#if {}\n".format( + " || ".join("!defined " + exclude for exclude in excludes) + ) + ) + output.write("{}({})\n".format(MACRO_NAMES[prop.type()], ", ".join(params))) + if excludes: + output.write("#endif\n") + + output.write( + """ +#ifdef DEFINED_CSS_PROP_ALIAS +#undef CSS_PROP_ALIAS +#undef DEFINED_CSS_PROP_ALIAS +#endif + +#ifdef DEFINED_CSS_PROP_SHORTHAND +#undef CSS_PROP_SHORTHAND +#undef DEFINED_CSS_PROP_SHORTHAND +#endif + +#ifdef DEFINED_CSS_PROP_LONGHAND +#undef CSS_PROP_LONGHAND +#undef DEFINED_CSS_PROP_LONGHAND +#endif + +#undef CSS_PROP_DOMPROP_PREFIXED +""" + ) diff --git a/layout/style/GlobalStyleSheetCache.cpp b/layout/style/GlobalStyleSheetCache.cpp new file mode 100644 index 0000000000..6a8507af7b --- /dev/null +++ b/layout/style/GlobalStyleSheetCache.cpp @@ -0,0 +1,561 @@ +/* -*- 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 "GlobalStyleSheetCache.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsExceptionHandler.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/ReferrerInfo.h" +#include "mozilla/dom/SRIMetadata.h" +#include "mozilla/ipc/SharedMemory.h" +#include "MainThreadUtils.h" +#include "nsContentUtils.h" +#include "nsIConsoleService.h" +#include "nsIFile.h" +#include "nsIObserverService.h" +#include "nsIXULRuntime.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" +#include "nsServiceManagerUtils.h" +#include "nsXULAppAPI.h" + +#include + +namespace mozilla { + +// The GlobalStyleSheetCache is responsible for sharing user agent style sheet +// contents across processes using shared memory. Here is a high level view of +// how that works: +// +// * In the parent process, in the GlobalStyleSheetCache constructor (which is +// called early on in a process' lifetime), we parse all UA style sheets into +// Gecko StyleSheet objects. +// +// * The constructor calls InitSharedSheetsInParent, which creates a shared +// memory segment that we know ahead of time will be big enough to store the +// UA sheets. +// +// * It then creates a Rust SharedMemoryBuilder object and passes it a pointer +// to the start of the shared memory. +// +// * For each UA sheet, we call Servo_SharedMemoryBuilder_AddStylesheet, which +// takes the StylesheetContents::rules (an Arc>), produces a +// deep clone of it, and writes that clone into the shared memory: +// +// * The deep clone isn't a clone() call, but a call to ToShmem::to_shmem. The +// ToShmem trait must be implemented on every type that is reachable under +// the Arc>. The to_shmem call for each type will clone the +// value, but any heap allocation will be cloned and placed into the shared +// memory buffer, rather than heap allocated. +// +// * For most types, the ToShmem implementation is simple, and we just +// #[derive(ToShmem)] it. For the types that need special handling due to +// having heap allocations (Vec, Box, Arc, etc.) we have impls that +// call to_shmem on the heap allocated data, and then create a new container +// (e.g. using Box::from_raw) that points into the shared memory. +// +// * Arc and Locked want to perform atomic writes on data that needs to +// be in the shared memory buffer (the reference count for Arc, and the +// SharedRwLock's AtomicRefCell for Locked), so we add special modes to +// those objects that skip the writes. For Arc, that means never +// dropping the object since we don't track the reference count. That's +// fine, since we want to just drop the entire shared memory buffer at +// shutdown. For Locked, we just panic on attempting to take the lock +// for writing. That's also fine, since we don't want devtools being able +// to modify UA sheets. +// +// * For Atoms in Rust, static atoms are represented by an index into the +// static atom table. Then if we need to Deref the Atom we look up the +// table. We panic if any Atom we encounter in the UA style sheets is +// not a static atom. +// +// * For each UA sheet, we create a new C++ StyleSheet object using the shared +// memory clone of the sheet contents, and throw away the original heap +// allocated one. (We could avoid creating a new C++ StyleSheet object +// wrapping the shared contents, and update the original StyleSheet object's +// contents, but it's doubtful that matters.) +// +// * When we initially map the shared memory in the parent process in +// InitSharedSheetsInParent, we choose an address which is far away from the +// current extent of the heap. Although not too far, since we don't want to +// unnecessarily fragment the virtual address space. +// +// * In the child process, as early as possible (in +// ContentChild::InitSharedUASheets), we try to map the shared memory at that +// same address, then pass the shared memory buffer to +// GlobalStyleSheetCache::SetSharedMemory. Since we map at the same +// address, this means any internal pointers in the UA sheets back into the +// shared memory buffer that were written by the parent process are valid in +// the child process too. +// +// * In practice, mapping at the address we need in the child process this works +// nearly all the time. If we fail to map at the address we need, the child +// process falls back to parsing and allocating its own copy of the UA sheets. + +using namespace mozilla; +using namespace css; + +#define PREF_LEGACY_STYLESHEET_CUSTOMIZATION \ + "toolkit.legacyUserProfileCustomizations.stylesheets" + +NS_IMPL_ISUPPORTS(GlobalStyleSheetCache, nsIObserver, nsIMemoryReporter) + +nsresult GlobalStyleSheetCache::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, "profile-before-change")) { + mUserContentSheet = nullptr; + mUserChromeSheet = nullptr; + } else if (!strcmp(aTopic, "profile-do-change")) { + InitFromProfile(); + } else { + MOZ_ASSERT_UNREACHABLE("Unexpected observer topic."); + } + return NS_OK; +} + +#define STYLE_SHEET(identifier_, url_, shared_) \ + NotNull GlobalStyleSheetCache::identifier_##Sheet() { \ + if (!m##identifier_##Sheet) { \ + m##identifier_##Sheet = LoadSheetURL(url_, eAgentSheetFeatures, eCrash); \ + } \ + return WrapNotNull(m##identifier_##Sheet); \ + } +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + +StyleSheet* GlobalStyleSheetCache::GetUserContentSheet() { + return mUserContentSheet; +} + +StyleSheet* GlobalStyleSheetCache::GetUserChromeSheet() { + return mUserChromeSheet; +} + +void GlobalStyleSheetCache::Shutdown() { + gCSSLoader = nullptr; + NS_WARNING_ASSERTION(!gStyleCache || !gUserContentSheetURL, + "Got the URL but never used?"); + gStyleCache = nullptr; + gUserContentSheetURL = nullptr; + for (auto& r : URLExtraData::sShared) { + r = nullptr; + } + // We want to leak the shared memory forever, rather than cleaning up all + // potential DOM references and such that chrome code may have created. +} + +void GlobalStyleSheetCache::SetUserContentCSSURL(nsIURI* aURI) { + MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes."); + gUserContentSheetURL = aURI; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf) + +NS_IMETHODIMP +GlobalStyleSheetCache::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) { + MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache/unshared", KIND_HEAP, + UNITS_BYTES, + SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf), + "Memory used for built-in style sheets that are not " + "shared between processes."); + + if (XRE_IsParentProcess()) { + MOZ_COLLECT_REPORT( + "explicit/layout/style-sheet-cache/shared", KIND_NONHEAP, UNITS_BYTES, + sSharedMemory ? sUsedSharedMemory : 0, + "Memory used for built-in style sheets that are shared to " + "child processes."); + } + + return NS_OK; +} + +size_t GlobalStyleSheetCache::SizeOfIncludingThis( + MallocSizeOf aMallocSizeOf) const { + size_t n = aMallocSizeOf(this); + +#define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0; + +#define STYLE_SHEET(identifier_, url_, shared_) MEASURE(m##identifier_##Sheet); +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + + MEASURE(mUserChromeSheet); + MEASURE(mUserContentSheet); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - gCSSLoader + + return n; +} + +GlobalStyleSheetCache::GlobalStyleSheetCache() { + nsCOMPtr obsSvc = services::GetObserverService(); + NS_ASSERTION(obsSvc, "No global observer service?"); + + if (obsSvc) { + obsSvc->AddObserver(this, "profile-before-change", false); + obsSvc->AddObserver(this, "profile-do-change", false); + } + + // Load user style sheets. + InitFromProfile(); + + if (XRE_IsParentProcess()) { + // We know we need xul.css for the UI, so load that now too: + XULSheet(); + } + + if (gUserContentSheetURL) { + MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes."); + mUserContentSheet = + LoadSheet(gUserContentSheetURL, eUserSheetFeatures, eLogToConsole); + gUserContentSheetURL = nullptr; + } + + // If we are the in the parent process, then we load all of the UA sheets that + // are shareable and store them into shared memory. In both the parent and + // the content process, we load these sheets out of shared memory. + // + // The shared memory buffer's format is a Header object, which contains + // internal pointers to each of the shared style sheets, followed by the style + // sheets themselves. + if (StaticPrefs::layout_css_shared_memory_ua_sheets_enabled()) { + if (XRE_IsParentProcess()) { + // Load the style sheets and store them in a new shared memory buffer. + InitSharedSheetsInParent(); + } else if (sSharedMemory) { + // Use the shared memory handle that was given to us by a SetSharedMemory + // call under ContentChild::InitXPCOM. + MOZ_ASSERT(sSharedMemory->memory(), + "GlobalStyleSheetCache::SetSharedMemory should have mapped " + "the shared memory"); + } + } + + // If we get here and we don't have a shared memory handle, then it means + // either we failed to create the shared memory buffer in the parent process + // (unexpected), or we failed to map the shared memory buffer at the address + // we needed in the content process (might happen). + // + // If sSharedMemory is non-null, but it is not currently mapped, then it means + // we are in the parent process, and we failed to re-map the memory after + // freezing it. (We keep sSharedMemory around so that we can still share it + // to content processes.) + // + // In the parent process, this means we'll just leave our eagerly loaded + // non-shared sheets in the mFooSheet fields. In a content process, we'll + // lazily load our own copies of the sheets later. + if (sSharedMemory) { + if (auto* header = static_cast(sSharedMemory->memory())) { + MOZ_RELEASE_ASSERT(header->mMagic == Header::kMagic); + +#define STYLE_SHEET(identifier_, url_, shared_) \ + if (shared_) { \ + LoadSheetFromSharedMemory(url_, &m##identifier_##Sheet, \ + eAgentSheetFeatures, header, \ + UserAgentStyleSheetID::identifier_); \ + } +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + } + } +} + +void GlobalStyleSheetCache::LoadSheetFromSharedMemory( + const char* aURL, RefPtr* aSheet, SheetParsingMode aParsingMode, + Header* aHeader, UserAgentStyleSheetID aSheetID) { + auto i = size_t(aSheetID); + + auto sheet = + MakeRefPtr(aParsingMode, CORS_NONE, dom::SRIMetadata()); + + nsCOMPtr uri; + MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aURL)); + + sheet->SetPrincipal(nsContentUtils::GetSystemPrincipal()); + sheet->SetURIs(uri, uri, uri); + nsCOMPtr referrerInfo = + dom::ReferrerInfo::CreateForExternalCSSResources(sheet); + sheet->SetReferrerInfo(referrerInfo); + sheet->SetSharedContents(aHeader->mSheets[i]); + sheet->SetComplete(); + URLExtraData::sShared[i] = sheet->URLData(); + + *aSheet = std::move(sheet); +} + +void GlobalStyleSheetCache::InitSharedSheetsInParent() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_RELEASE_ASSERT(!sSharedMemory); + + auto shm = MakeUnique(); + if (NS_WARN_IF(!shm->CreateFreezeable(kSharedMemorySize))) { + return; + } + + // We need to choose an address to map the shared memory in the parent process + // that we'll also be able to use in content processes. There's no way to + // pick an address that is guaranteed to be free in future content processes, + // so instead we pick an address that is some distance away from current heap + // allocations and hope that by the time the content process maps the shared + // memory, that address will be free. + // + // On 64 bit, we have a large amount of address space, so we pick an address + // half way through the next 8 GiB of free space, and this has a very good + // chance of succeeding. On 32 bit, address space is more constrained. We + // only have 3 GiB of space to work with, and we don't want to pick a location + // right in the middle, since that could cause future large allocations to + // fail. So we pick an address half way through the next 512 MiB of free + // space. Experimentally this seems to work 9 times out of 10; this is good + // enough, as it means only 1 in 10 content processes will have its own unique + // copies of the UA style sheets, and we're still getting a significant + // overall memory saving. + // + // In theory ASLR could reduce the likelihood of the mapping succeeding in + // content processes, due to our expectations of where the heap is being + // wrong, but in practice this isn't an issue. +#ifdef HAVE_64BIT_BUILD + constexpr size_t kOffset = 0x200000000ULL; // 8 GiB +#else + constexpr size_t kOffset = 0x20000000; // 512 MiB +#endif + + void* address = nullptr; + if (void* p = base::SharedMemory::FindFreeAddressSpace(2 * kOffset)) { + address = reinterpret_cast(uintptr_t(p) + kOffset); + } + + if (!shm->Map(kSharedMemorySize, address)) { + // Failed to map at the address we computed for some reason. Fall back + // to just allocating at a location of the OS's choosing, and hope that + // it works in the content process. + if (NS_WARN_IF(!shm->Map(kSharedMemorySize))) { + return; + } + } + address = shm->memory(); + + auto* header = static_cast(address); + header->mMagic = Header::kMagic; +#ifdef DEBUG + for (const auto* ptr : header->mSheets) { + MOZ_RELEASE_ASSERT(!ptr, "expected shared memory to have been zeroed"); + } +#endif + + UniquePtr builder(Servo_SharedMemoryBuilder_Create( + header->mBuffer, kSharedMemorySize - offsetof(Header, mBuffer))); + + nsCString message; + + // Copy each one into the shared memory, and record its pointer. + // + // Normally calling ToShared on UA sheets should not fail. It happens + // in practice in odd cases that seem like corrupted installations; see bug + // 1621773. On failure, return early and fall back to non-shared sheets. +#define STYLE_SHEET(identifier_, url_, shared_) \ + if (shared_) { \ + StyleSheet* sheet = identifier_##Sheet(); \ + size_t i = size_t(UserAgentStyleSheetID::identifier_); \ + URLExtraData::sShared[i] = sheet->URLData(); \ + header->mSheets[i] = sheet->ToShared(builder.get(), message); \ + if (!header->mSheets[i]) { \ + CrashReporter::AppendAppNotesToCrashReport("\n"_ns + message); \ + return; \ + } \ + } +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + + // Finished writing into the shared memory. Freeze it, so that a process + // can't confuse other processes by changing the UA style sheet contents. + if (NS_WARN_IF(!shm->Freeze())) { + return; + } + + // The Freeze() call unmaps the shared memory. Re-map it again as read only. + // If this fails, due to something else being mapped into the same place + // between the Freeze() and Map() call, we can just fall back to keeping our + // own copy of the UA style sheets in the parent, and still try sending the + // shared memory to the content processes. + shm->Map(kSharedMemorySize, address); + + // Record how must of the shared memory we have used, for memory reporting + // later. We round up to the nearest page since the free space at the end + // of the page isn't really usable for anything else. + // + // TODO(heycam): This won't be true on Windows unless we allow creating the + // shared memory with SEC_RESERVE so that the pages are reserved but not + // committed. + size_t pageSize = ipc::SharedMemory::SystemPageSize(); + sUsedSharedMemory = + (Servo_SharedMemoryBuilder_GetLength(builder.get()) + pageSize - 1) & + ~(pageSize - 1); + + sSharedMemory = shm.release(); +} + +GlobalStyleSheetCache::~GlobalStyleSheetCache() { + UnregisterWeakMemoryReporter(this); +} + +void GlobalStyleSheetCache::InitMemoryReporter() { + RegisterWeakMemoryReporter(this); +} + +/* static */ +GlobalStyleSheetCache* GlobalStyleSheetCache::Singleton() { + MOZ_ASSERT(NS_IsMainThread()); + + if (!gStyleCache) { + gStyleCache = new GlobalStyleSheetCache; + gStyleCache->InitMemoryReporter(); + + // For each pref that controls a CSS feature that a UA style sheet depends + // on (such as a pref that enables a property that a UA style sheet uses), + // register DependentPrefChanged as a callback to ensure that the relevant + // style sheets will be re-parsed. + // Preferences::RegisterCallback(&DependentPrefChanged, + // "layout.css.example-pref.enabled"); + } + + return gStyleCache; +} + +void GlobalStyleSheetCache::InitFromProfile() { + if (!Preferences::GetBool(PREF_LEGACY_STYLESHEET_CUSTOMIZATION)) { + return; + } + + nsCOMPtr appInfo = + do_GetService("@mozilla.org/xre/app-info;1"); + if (appInfo) { + bool inSafeMode = false; + appInfo->GetInSafeMode(&inSafeMode); + if (inSafeMode) return; + } + nsCOMPtr contentFile; + nsCOMPtr chromeFile; + + NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(contentFile)); + if (!contentFile) { + // if we don't have a profile yet, that's OK! + return; + } + + contentFile->Clone(getter_AddRefs(chromeFile)); + if (!chromeFile) return; + + contentFile->Append(u"userContent.css"_ns); + chromeFile->Append(u"userChrome.css"_ns); + + mUserContentSheet = LoadSheetFile(contentFile, eUserSheetFeatures); + mUserChromeSheet = LoadSheetFile(chromeFile, eUserSheetFeatures); +} + +RefPtr GlobalStyleSheetCache::LoadSheetURL( + const char* aURL, SheetParsingMode aParsingMode, + FailureAction aFailureAction) { + nsCOMPtr uri; + NS_NewURI(getter_AddRefs(uri), aURL); + return LoadSheet(uri, aParsingMode, aFailureAction); +} + +RefPtr GlobalStyleSheetCache::LoadSheetFile( + nsIFile* aFile, SheetParsingMode aParsingMode) { + bool exists = false; + aFile->Exists(&exists); + if (!exists) { + return nullptr; + } + + nsCOMPtr uri; + NS_NewFileURI(getter_AddRefs(uri), aFile); + return LoadSheet(uri, aParsingMode, eLogToConsole); +} + +static void ErrorLoadingSheet(nsIURI* aURI, const char* aMsg, + FailureAction aFailureAction) { + nsPrintfCString errorMessage("%s loading built-in stylesheet '%s'", aMsg, + aURI ? aURI->GetSpecOrDefault().get() : ""); + if (aFailureAction == eLogToConsole) { + nsCOMPtr cs = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) { + cs->LogStringMessage(NS_ConvertUTF8toUTF16(errorMessage).get()); + return; + } + } + + MOZ_CRASH_UNSAFE(errorMessage.get()); +} + +RefPtr GlobalStyleSheetCache::LoadSheet( + nsIURI* aURI, SheetParsingMode aParsingMode, FailureAction aFailureAction) { + if (!aURI) { + ErrorLoadingSheet(aURI, "null URI", eCrash); + return nullptr; + } + + if (!gCSSLoader) { + gCSSLoader = new Loader; + } + + auto result = gCSSLoader->LoadSheetSync(aURI, aParsingMode, + css::Loader::UseSystemPrincipal::Yes); + if (MOZ_UNLIKELY(result.isErr())) { + ErrorLoadingSheet( + aURI, + nsPrintfCString("LoadSheetSync failed with error %" PRIx32, + static_cast(result.unwrapErr())) + .get(), + aFailureAction); + } + return result.unwrapOr(nullptr); +} + +/* static */ void GlobalStyleSheetCache::SetSharedMemory( + base::SharedMemoryHandle aHandle, uintptr_t aAddress) { + MOZ_ASSERT(!XRE_IsParentProcess()); + MOZ_ASSERT(!gStyleCache, "Too late, GlobalStyleSheetCache already created!"); + MOZ_ASSERT(!sSharedMemory, "Shouldn't call this more than once"); + + auto shm = MakeUnique(); + if (!shm->SetHandle(std::move(aHandle), /* read_only */ true)) { + return; + } + + if (shm->Map(kSharedMemorySize, reinterpret_cast(aAddress))) { + sSharedMemory = shm.release(); + } +} + +base::SharedMemoryHandle GlobalStyleSheetCache::CloneHandle() { + MOZ_ASSERT(XRE_IsParentProcess()); + if (sSharedMemory) { + return sSharedMemory->CloneHandle(); + } + return nullptr; +} + +StaticRefPtr GlobalStyleSheetCache::gStyleCache; +StaticRefPtr GlobalStyleSheetCache::gCSSLoader; +StaticRefPtr GlobalStyleSheetCache::gUserContentSheetURL; + +StaticAutoPtr GlobalStyleSheetCache::sSharedMemory; +size_t GlobalStyleSheetCache::sUsedSharedMemory; + +} // namespace mozilla diff --git a/layout/style/GlobalStyleSheetCache.h b/layout/style/GlobalStyleSheetCache.h new file mode 100644 index 0000000000..688729fede --- /dev/null +++ b/layout/style/GlobalStyleSheetCache.h @@ -0,0 +1,132 @@ +/* -*- 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_GlobalStyleSheetCache_h__ +#define mozilla_GlobalStyleSheetCache_h__ + +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "base/shared_memory.h" +#include "mozilla/Attributes.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/PreferenceSheet.h" +#include "mozilla/NotNull.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/UserAgentStyleSheetID.h" +#include "mozilla/css/Loader.h" + +class nsIFile; +class nsIURI; + +namespace mozilla { +class CSSStyleSheet; +} // namespace mozilla + +namespace mozilla { +namespace css { + +// Enum defining how error should be handled. +enum FailureAction { eCrash = 0, eLogToConsole }; + +} // namespace css + +class GlobalStyleSheetCache final : public nsIObserver, + public nsIMemoryReporter { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSIMEMORYREPORTER + + static GlobalStyleSheetCache* Singleton(); + +#define STYLE_SHEET(identifier_, url_, shared_) \ + NotNull identifier_##Sheet(); +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + + StyleSheet* GetUserContentSheet(); + StyleSheet* GetUserChromeSheet(); + + static void Shutdown(); + + static void SetUserContentCSSURL(nsIURI* aURI); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + // Set the shared memory segment to load the shared UA sheets from. + // Called early on in a content process' life from + // ContentChild::InitSharedUASheets, before the GlobalStyleSheetCache + // singleton has been created. + static void SetSharedMemory(base::SharedMemoryHandle aHandle, + uintptr_t aAddress); + + // Obtain a shared memory handle for the shared UA sheets to pass into a + // content process. Called by ContentParent::InitInternal shortly after + // a content process has been created. + base::SharedMemoryHandle CloneHandle(); + + // Returns the address of the shared memory segment that holds the shared UA + // sheets. + uintptr_t GetSharedMemoryAddress() { + return sSharedMemory ? uintptr_t(sSharedMemory->memory()) : 0; + } + + // Size of the shared memory buffer we'll create to store the shared UA + // sheets. We choose a value that is big enough on both 64 bit and 32 bit. + // + // If this isn't big enough for the current contents of the shared UA + // sheets, we'll crash under InitSharedSheetsInParent. + static constexpr size_t kSharedMemorySize = 1024 * 450; + + private: + // Shared memory header. + struct Header { + static constexpr uint32_t kMagic = 0x55415353; + uint32_t mMagic; // Must be set to kMagic. + const StyleLockedCssRules* mSheets[size_t(UserAgentStyleSheetID::Count)]; + uint8_t mBuffer[1]; + }; + + GlobalStyleSheetCache(); + ~GlobalStyleSheetCache(); + + void InitFromProfile(); + void InitSharedSheetsInParent(); + void InitMemoryReporter(); + RefPtr LoadSheetURL(const char* aURL, + css::SheetParsingMode aParsingMode, + css::FailureAction aFailureAction); + RefPtr LoadSheetFile(nsIFile* aFile, + css::SheetParsingMode aParsingMode); + RefPtr LoadSheet(nsIURI* aURI, css::SheetParsingMode aParsingMode, + css::FailureAction aFailureAction); + void LoadSheetFromSharedMemory(const char* aURL, RefPtr* aSheet, + css::SheetParsingMode, Header*, + UserAgentStyleSheetID); + + static StaticRefPtr gStyleCache; + static StaticRefPtr gCSSLoader; + static StaticRefPtr gUserContentSheetURL; + +#define STYLE_SHEET(identifier_, url_, shared_) \ + RefPtr m##identifier_##Sheet; +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + + RefPtr mUserChromeSheet; + RefPtr mUserContentSheet; + + // Shared memory segment storing shared style sheets. + static StaticAutoPtr sSharedMemory; + + // How much of the shared memory buffer we ended up using. Used for memory + // reporting in the parent process. + static size_t sUsedSharedMemory; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/GroupRule.cpp b/layout/style/GroupRule.cpp new file mode 100644 index 0000000000..ff3cd11f8c --- /dev/null +++ b/layout/style/GroupRule.cpp @@ -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/. */ + +/* + * internal interface representing CSS style rules that contain other + * rules, such as @media rules + */ + +#include "mozilla/css/GroupRule.h" + +#include "mozilla/dom/CSSRuleList.h" + +#include "nsPrintfCString.h" + +using namespace mozilla::dom; + +namespace mozilla::css { + +GroupRule::GroupRule(StyleSheet* aSheet, Rule* aParentRule, + uint32_t aLineNumber, uint32_t aColumnNumber) + : Rule(aSheet, aParentRule, aLineNumber, aColumnNumber) {} + +GroupRule::~GroupRule() { + MOZ_ASSERT(!mSheet, "SetStyleSheet should have been called"); + if (mRuleList) { + mRuleList->DropReferences(); + } +} + +NS_IMPL_ADDREF_INHERITED(GroupRule, Rule) +NS_IMPL_RELEASE_INHERITED(GroupRule, Rule) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule) +NS_INTERFACE_MAP_END_INHERITING(Rule) + +bool GroupRule::IsCCLeaf() const { + if (!Rule::IsCCLeaf()) { + return false; + } + return !mRuleList; +} + +ServoCSSRuleList* GroupRule::CssRules() { + if (!mRuleList) { + // Lazily create the rule list since most style rules won't have child + // rules. + mRuleList = + new ServoCSSRuleList(GetOrCreateRawRules(), GetStyleSheet(), this); + } + return mRuleList; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GroupRule, Rule) + if (tmp->mRuleList) { + // If tmp has a style sheet (which can happen if it gets unlinked + // earlier than its owning style sheet), then we need to null out the + // style sheet pointer on descendants now, before we clear mRuleList. + tmp->mRuleList->DropReferences(); + tmp->mRuleList = nullptr; + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GroupRule, Rule) + ImplCycleCollectionTraverse(cb, tmp->mRuleList, "mRuleList"); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +#ifdef DEBUG +void GroupRule::List(FILE* out, int32_t aIndent) const { + // TODO list something reasonable? +} +#endif + +/* virtual */ +void GroupRule::DropSheetReference() { + if (mRuleList) { + mRuleList->DropSheetReference(); + } + Rule::DropSheetReference(); +} + +uint32_t GroupRule::InsertRule(const nsACString& aRule, uint32_t aIndex, + ErrorResult& aRv) { + if (IsReadOnly()) { + return 0; + } + + StyleSheet* sheet = GetStyleSheet(); + if (NS_WARN_IF(!sheet)) { + aRv.Throw(NS_ERROR_FAILURE); + return 0; + } + + uint32_t count = StyleRuleCount(); + if (aIndex > count) { + aRv.ThrowIndexSizeError(nsPrintfCString( + "Can't insert rule at index %u because rule list length is %u", aIndex, + count)); + return 0; + } + + NS_ASSERTION(count <= INT32_MAX, "Too many style rules!"); + + nsresult rv = sheet->InsertRuleIntoGroup(aRule, this, aIndex); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return 0; + } + return aIndex; +} + +void GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv) { + if (IsReadOnly()) { + return; + } + + StyleSheet* sheet = GetStyleSheet(); + if (NS_WARN_IF(!sheet)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + uint32_t count = StyleRuleCount(); + if (aIndex >= count) { + aRv.ThrowIndexSizeError(nsPrintfCString( + "Index %u is too large for list of length %u", aIndex, count)); + return; + } + + NS_ASSERTION(count <= INT32_MAX, "Too many style rules!"); + + nsresult rv = sheet->DeleteRuleFromGroup(this, aIndex); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + } +} + +size_t GroupRule::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { + // TODO how to implement? + return 0; +} + +} // namespace mozilla::css diff --git a/layout/style/GroupRule.h b/layout/style/GroupRule.h new file mode 100644 index 0000000000..1584efd9b1 --- /dev/null +++ b/layout/style/GroupRule.h @@ -0,0 +1,96 @@ +/* -*- 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/. */ + +/* + * internal interface representing CSS style rules that contain other + * rules, such as @media rules + */ + +#ifndef mozilla_css_GroupRule_h__ +#define mozilla_css_GroupRule_h__ + +#include "mozilla/Attributes.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ServoCSSRuleList.h" +#include "mozilla/css/Rule.h" +#include "nsCycleCollectionParticipant.h" + +namespace mozilla { + +class ErrorResult; +class StyleSheet; + +namespace dom { +class CSSRuleList; +} // namespace dom + +namespace css { + +// Inherits from Rule so it can be shared between MediaRule and DocumentRule +class GroupRule : public Rule { + protected: + GroupRule(StyleSheet* aSheet, Rule* aParentRule, uint32_t aLineNumber, + uint32_t aColumnNumber); + virtual ~GroupRule(); + virtual already_AddRefed GetOrCreateRawRules() = 0; + + public: + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GroupRule, Rule) + NS_DECL_ISUPPORTS_INHERITED + + GroupRule(const GroupRule&) = delete; + bool IsCCLeaf() const override; + + bool IsGroupRule() const final { return true; } + +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + void DropSheetReference() override; + uint32_t StyleRuleCount() { return CssRules()->Length(); } + Rule* GetStyleRuleAt(int32_t aIndex) { return CssRules()->GetRule(aIndex); } + + void DidSetRawAfterClone() { + if (mRuleList) { + mRuleList->SetRawAfterClone(GetOrCreateRawRules()); + } + } + + /* + * The next method should never be called unless you have first called + * WillDirty() on the parent stylesheet. + */ + nsresult DeleteStyleRuleAt(uint32_t aIndex) { + return CssRules()->DeleteRule(aIndex); + } + + // non-virtual -- it is only called by subclasses + size_t SizeOfExcludingThis(MallocSizeOf) const; + size_t SizeOfIncludingThis(MallocSizeOf) const override = 0; + + // WebIDL API + ServoCSSRuleList* CssRules(); + uint32_t InsertRule(const nsACString& aRule, uint32_t aIndex, + ErrorResult& aRv); + void DeleteRule(uint32_t aIndex, ErrorResult& aRv); + + private: + RefPtr mRuleList; +}; + +// Implementation of WebIDL CSSConditionRule. +class ConditionRule : public GroupRule { + protected: + using GroupRule::GroupRule; + + public: + virtual void GetConditionText(nsACString& aConditionText) = 0; +}; + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_GroupRule_h__ */ diff --git a/layout/style/ImageDocument.css b/layout/style/ImageDocument.css new file mode 100644 index 0000000000..5cd22c4b5c --- /dev/null +++ b/layout/style/ImageDocument.css @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +/* + * This CSS stylesheet defines the rules to be applied to any ImageDocuments, + * including those in frames. +*/ + +body { + /* To give the image access to our document's full viewport, we need to + override the margin that the html.css UA stylesheet would otherwise apply + to our body. */ + margin: 0; +} + +@media not print { + .fullZoomOut { + cursor: zoom-out; + } + + .fullZoomIn { + cursor: zoom-in; + } + + .shrinkToFit { + cursor: zoom-in; + } + + .overflowingVertical, .overflowingHorizontalOnly { + cursor: zoom-out; + } +} + +.isInObjectOrEmbed { + width: 100%; + height: 100vh; +} + +img { + display: block; +} diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp new file mode 100644 index 0000000000..2ae1822b02 --- /dev/null +++ b/layout/style/ImageLoader.cpp @@ -0,0 +1,834 @@ +/* -*- 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 class that handles style system image loads (other image loads are handled + * by the nodes in the content tree). + */ + +#include "mozilla/css/ImageLoader.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/LargestContentfulPaint.h" +#include "mozilla/dom/ImageTracker.h" +#include "nsContentUtils.h" +#include "nsIReflowCallback.h" +#include "nsLayoutUtils.h" +#include "nsError.h" +#include "nsCanvasFrame.h" +#include "nsDisplayList.h" +#include "nsIFrameInlines.h" +#include "imgIContainer.h" +#include "imgINotificationObserver.h" +#include "Image.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/SVGObserverUtils.h" +#include "mozilla/layers/WebRenderUserData.h" +#include "nsTHashSet.h" + +using namespace mozilla::dom; + +namespace mozilla::css { + +// This is a singleton observer which looks in the `GlobalRequestTable` to look +// at which loaders to notify. +struct GlobalImageObserver final : public imgINotificationObserver { + NS_DECL_ISUPPORTS + NS_DECL_IMGINOTIFICATIONOBSERVER + + GlobalImageObserver() = default; + + private: + virtual ~GlobalImageObserver() = default; +}; + +NS_IMPL_ADDREF(GlobalImageObserver) +NS_IMPL_RELEASE(GlobalImageObserver) + +NS_INTERFACE_MAP_BEGIN(GlobalImageObserver) + NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) +NS_INTERFACE_MAP_END + +// Data associated with every started load. +struct ImageTableEntry { + // Set of all ImageLoaders that have registered this URL and care for updates + // for it. + nsTHashSet mImageLoaders; + + // The amount of style values that are sharing this image. + uint32_t mSharedCount = 1; +}; + +using GlobalRequestTable = + nsClassHashtable, ImageTableEntry>; + +// A table of all loads, keyed by their id mapping them to the set of +// ImageLoaders they have been registered in, and recording their "canonical" +// image request. +// +// We use the load id as the key since we can only access sImages on the +// main thread, but LoadData objects might be destroyed from other threads, +// and we don't want to leave dangling pointers around. +static StaticAutoPtr sImages; +static StaticRefPtr sImageObserver; + +/* static */ +void ImageLoader::Init() { + sImages = new GlobalRequestTable(); + sImageObserver = new GlobalImageObserver(); +} + +/* static */ +void ImageLoader::Shutdown() { + for (const auto& entry : *sImages) { + imgIRequest* imgRequest = entry.GetKey(); + // All the images we put in sImages are imgRequestProxy, see LoadImage, but + // it's non-trivial to make the hash table to use that without changing a + // lot of other code. + auto* req = static_cast(imgRequest); + req->SetCancelable(true); + req->CancelAndForgetObserver(NS_BINDING_ABORTED); + } + + sImages = nullptr; + sImageObserver = nullptr; +} + +void ImageLoader::DropDocumentReference() { + MOZ_ASSERT(NS_IsMainThread()); + + // It's okay if GetPresContext returns null here (due to the presshell pointer + // on the document being null) as that means the presshell has already + // been destroyed, and it also calls ClearFrames when it is destroyed. + ClearFrames(GetPresContext()); + + mDocument = nullptr; +} + +// Arrays of requests and frames are sorted by their pointer address, +// for faster lookup. +template > +static size_t GetMaybeSortedIndex(const nsTArray& aArray, + const Item& aItem, bool* aFound, + Comparator aComparator = Comparator()) { + size_t index = aArray.IndexOfFirstElementGt(aItem, aComparator); + *aFound = index > 0 && aComparator.Equals(aItem, aArray.ElementAt(index - 1)); + return index; +} + +// Returns true if an async decode is triggered for aRequest, and thus we will +// get an OnFrameComplete callback for this request eventually. +static bool TriggerAsyncDecodeAtIntrinsicSize(imgIRequest* aRequest) { + uint32_t status = 0; + // Don't block onload if we've already got a frame complete status + // (since in that case the image is already loaded), or if we get an + // error status (since then we know the image won't ever load). + if (NS_SUCCEEDED(aRequest->GetImageStatus(&status))) { + if (status & imgIRequest::STATUS_FRAME_COMPLETE) { + // Already decoded, no need to do it again. + return false; + } + if (status & imgIRequest::STATUS_ERROR) { + // Already errored, this would be useless. + return false; + } + } + + // We want to request decode in such a way that avoids triggering sync decode. + // First, we attempt to convert the aRequest into a imgIContainer. If that + // succeeds, then aRequest has an image and we can request decoding for size + // at zero size, the size will be ignored because we don't pass the + // FLAG_HIGH_QUALITY_SCALING flag and an async decode (because we didn't pass + // any sync decoding flags) at the intrinsic size will be requested. If the + // conversion to imgIContainer is unsuccessful, then that means aRequest + // doesn't have an image yet, which means we can safely call StartDecoding() + // on it without triggering any synchronous work. + nsCOMPtr imgContainer; + aRequest->GetImage(getter_AddRefs(imgContainer)); + if (imgContainer) { + imgContainer->RequestDecodeForSize(gfx::IntSize(0, 0), + imgIContainer::DECODE_FLAGS_DEFAULT); + } else { + // It's safe to call StartDecoding directly, since it can't + // trigger synchronous decode without an image. Flags are ignored. + aRequest->StartDecoding(imgIContainer::FLAG_NONE); + } + return true; +} + +void ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, + nsIFrame* aFrame, Flags aFlags) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!(aFlags & Flags::IsBlockingLoadEvent), + "Shouldn't be used in the public API"); + + { + nsCOMPtr observer; + aRequest->GetNotificationObserver(getter_AddRefs(observer)); + if (!observer) { + // The request has already been canceled, so ignore it. This is ok because + // we're not going to get any more notifications from a canceled request. + return; + } + MOZ_ASSERT(observer == sImageObserver); + } + + auto* const frameSet = + mRequestToFrameMap + .LookupOrInsertWith( + aRequest, + [&] { + mDocument->ImageTracker()->Add(aRequest); + + if (auto entry = sImages->Lookup(aRequest)) { + DebugOnly inserted = + entry.Data()->mImageLoaders.EnsureInserted(this); + MOZ_ASSERT(inserted); + } else { + MOZ_ASSERT_UNREACHABLE( + "Shouldn't be associating images not in sImages"); + } + + if (nsPresContext* presContext = GetPresContext()) { + nsLayoutUtils::RegisterImageRequestIfAnimated( + presContext, aRequest, nullptr); + } + return MakeUnique(); + }) + .get(); + + auto* const requestSet = + mFrameToRequestMap + .LookupOrInsertWith(aFrame, + [=]() { + aFrame->SetHasImageRequest(true); + return MakeUnique(); + }) + .get(); + + // Add frame to the frameSet, and handle any special processing the + // frame might require. + FrameWithFlags fwf(aFrame); + FrameWithFlags* fwfToModify = &fwf; + + // See if the frameSet already has this frame. + bool found; + uint32_t i = + GetMaybeSortedIndex(*frameSet, fwf, &found, FrameOnlyComparator()); + if (found) { + // We're already tracking this frame, so prepare to modify the + // existing FrameWithFlags object. + fwfToModify = &frameSet->ElementAt(i - 1); + } + + // Check if the frame requires special processing. + if (aFlags & Flags::RequiresReflowOnSizeAvailable) { + MOZ_ASSERT(!(aFlags & + Flags::RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking), + "These two are exclusive"); + fwfToModify->mFlags |= Flags::RequiresReflowOnSizeAvailable; + } + + if (aFlags & Flags::RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking) { + fwfToModify->mFlags |= + Flags::RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking; + + // If we weren't already blocking onload, do that now. + if (!(fwfToModify->mFlags & Flags::IsBlockingLoadEvent)) { + if (TriggerAsyncDecodeAtIntrinsicSize(aRequest)) { + // If there's no error, and the image has not loaded yet, so we can + // block onload. + fwfToModify->mFlags |= Flags::IsBlockingLoadEvent; + + // Block document onload until we either remove the frame in + // RemoveRequestToFrameMapping or onLoadComplete, or complete a reflow. + mDocument->BlockOnload(); + } + } + } + + // Do some sanity checking to ensure that we only add to one mapping + // iff we also add to the other mapping. + DebugOnly didAddToFrameSet(false); + DebugOnly didAddToRequestSet(false); + + // If we weren't already tracking this frame, add it to the frameSet. + if (!found) { + frameSet->InsertElementAt(i, fwf); + didAddToFrameSet = true; + } + + // Add request to the request set if it wasn't already there. + i = GetMaybeSortedIndex(*requestSet, aRequest, &found); + if (!found) { + requestSet->InsertElementAt(i, aRequest); + didAddToRequestSet = true; + } + + MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet, + "We should only add to one map iff we also add to the other map."); +} + +void ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, + nsIFrame* aFrame) { +#ifdef DEBUG + { + nsCOMPtr observer; + aRequest->GetNotificationObserver(getter_AddRefs(observer)); + MOZ_ASSERT(!observer || observer == sImageObserver); + } +#endif + + if (auto entry = mRequestToFrameMap.Lookup(aRequest)) { + const auto& frameSet = entry.Data(); + MOZ_ASSERT(frameSet, "This should never be null"); + + // Before we remove aFrame from the frameSet, unblock onload if needed. + bool found; + uint32_t i = GetMaybeSortedIndex(*frameSet, FrameWithFlags(aFrame), &found, + FrameOnlyComparator()); + if (found) { + UnblockOnloadIfNeeded(frameSet->ElementAt(i - 1)); + frameSet->RemoveElementAtUnsafe(i - 1); + } + + if (frameSet->IsEmpty()) { + DeregisterImageRequest(aRequest, GetPresContext()); + entry.Remove(); + } + } +} + +void ImageLoader::DeregisterImageRequest(imgIRequest* aRequest, + nsPresContext* aPresContext) { + mDocument->ImageTracker()->Remove(aRequest); + + if (auto entry = sImages->Lookup(aRequest)) { + entry.Data()->mImageLoaders.EnsureRemoved(this); + } + + if (aPresContext) { + nsLayoutUtils::DeregisterImageRequest(aPresContext, aRequest, nullptr); + } +} + +void ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest, + nsIFrame* aFrame) { + if (auto entry = mFrameToRequestMap.Lookup(aFrame)) { + const auto& requestSet = entry.Data(); + MOZ_ASSERT(requestSet, "This should never be null"); + requestSet->RemoveElementSorted(aRequest); + if (requestSet->IsEmpty()) { + aFrame->SetHasImageRequest(false); + entry.Remove(); + } + } +} + +void ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest, + nsIFrame* aFrame) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?"); + + RemoveRequestToFrameMapping(aRequest, aFrame); + RemoveFrameToRequestMapping(aRequest, aFrame); +} + +void ImageLoader::DropRequestsForFrame(nsIFrame* aFrame) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?"); + + UniquePtr requestSet; + mFrameToRequestMap.Remove(aFrame, &requestSet); + aFrame->SetHasImageRequest(false); + if (MOZ_UNLIKELY(!requestSet)) { + MOZ_ASSERT_UNREACHABLE("HasImageRequest was lying"); + return; + } + for (imgIRequest* request : *requestSet) { + RemoveRequestToFrameMapping(request, aFrame); + } +} + +void ImageLoader::SetAnimationMode(uint16_t aMode) { + MOZ_ASSERT(NS_IsMainThread()); + NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode || + aMode == imgIContainer::kDontAnimMode || + aMode == imgIContainer::kLoopOnceAnimMode, + "Wrong Animation Mode is being set!"); + + for (nsISupports* key : mRequestToFrameMap.Keys()) { + auto* request = static_cast(key); + +#ifdef DEBUG + { + nsCOMPtr debugRequest = request; + NS_ASSERTION(debugRequest == request, "This is bad"); + } +#endif + + nsCOMPtr container; + request->GetImage(getter_AddRefs(container)); + if (!container) { + continue; + } + + // This can fail if the image is in error, and we don't care. + container->SetAnimationMode(aMode); + } +} + +void ImageLoader::ClearFrames(nsPresContext* aPresContext) { + MOZ_ASSERT(NS_IsMainThread()); + + for (const auto& key : mRequestToFrameMap.Keys()) { + auto* request = static_cast(key); + +#ifdef DEBUG + { + nsCOMPtr debugRequest = request; + NS_ASSERTION(debugRequest == request, "This is bad"); + } +#endif + + DeregisterImageRequest(request, aPresContext); + } + + mRequestToFrameMap.Clear(); + mFrameToRequestMap.Clear(); +} + +static CORSMode EffectiveCorsMode(nsIURI* aURI, + const StyleComputedImageUrl& aImage) { + MOZ_ASSERT(aURI); + StyleCorsMode mode = aImage.CorsMode(); + if (mode == StyleCorsMode::None) { + return CORSMode::CORS_NONE; + } + MOZ_ASSERT(mode == StyleCorsMode::Anonymous); + if (aURI->SchemeIs("resource")) { + return CORSMode::CORS_NONE; + } + return CORSMode::CORS_ANONYMOUS; +} + +/* static */ +already_AddRefed ImageLoader::LoadImage( + const StyleComputedImageUrl& aImage, Document& aDocument) { + MOZ_ASSERT(NS_IsMainThread()); + nsIURI* uri = aImage.GetURI(); + if (!uri) { + return nullptr; + } + + if (aImage.HasRef()) { + bool isEqualExceptRef = false; + nsIURI* docURI = aDocument.GetDocumentURI(); + if (NS_SUCCEEDED(uri->EqualsExceptRef(docURI, &isEqualExceptRef)) && + isEqualExceptRef) { + // Prevent loading an internal resource. + return nullptr; + } + } + + int32_t loadFlags = + nsIRequest::LOAD_NORMAL | + nsContentUtils::CORSModeToLoadImageFlags(EffectiveCorsMode(uri, aImage)); + + const URLExtraData& data = aImage.ExtraData(); + + RefPtr request; + nsresult rv = nsContentUtils::LoadImage( + uri, &aDocument, &aDocument, data.Principal(), 0, data.ReferrerInfo(), + sImageObserver, loadFlags, u"css"_ns, getter_AddRefs(request)); + + if (NS_FAILED(rv) || !request) { + return nullptr; + } + + // This image could be shared across documents, so its load cannot be + // canceled, see bug 1800979. + request->SetCancelable(false); + sImages->GetOrInsertNew(request); + return request.forget(); +} + +void ImageLoader::UnloadImage(imgRequestProxy* aImage) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aImage); + + if (MOZ_UNLIKELY(!sImages)) { + return; // Shutdown() takes care of it. + } + + auto lookup = sImages->Lookup(aImage); + MOZ_DIAGNOSTIC_ASSERT(lookup, "Unregistered image?"); + if (MOZ_UNLIKELY(!lookup)) { + return; + } + + if (MOZ_UNLIKELY(--lookup.Data()->mSharedCount)) { + // Someone else still cares about this image. + return; + } + + // Now we want to really cancel the request. + aImage->SetCancelable(true); + aImage->CancelAndForgetObserver(NS_BINDING_ABORTED); + MOZ_DIAGNOSTIC_ASSERT(lookup.Data()->mImageLoaders.IsEmpty(), + "Shouldn't be keeping references to any loader " + "by now"); + lookup.Remove(); +} + +void ImageLoader::NoteSharedLoad(imgRequestProxy* aImage) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aImage); + + auto lookup = sImages->Lookup(aImage); + MOZ_DIAGNOSTIC_ASSERT(lookup, "Unregistered image?"); + if (MOZ_UNLIKELY(!lookup)) { + return; + } + + lookup.Data()->mSharedCount++; +} + +nsPresContext* ImageLoader::GetPresContext() { + if (!mDocument) { + return nullptr; + } + + return mDocument->GetPresContext(); +} + +static bool IsRenderNoImages(uint32_t aDisplayItemKey) { + DisplayItemType type = GetDisplayItemTypeFromKey(aDisplayItemKey); + uint8_t flags = GetDisplayItemFlagsForType(type); + return flags & TYPE_RENDERS_NO_IMAGES; +} + +static void InvalidateImages(nsIFrame* aFrame, imgIRequest* aRequest, + bool aForcePaint) { + if (!aFrame->StyleVisibility()->IsVisible()) { + return; + } + + if (aFrame->IsTablePart()) { + // Tables don't necessarily build border/background display items + // for the individual table part frames, so IterateRetainedDataFor + // might not find the right display item. + return aFrame->InvalidateFrame(); + } + + if (aFrame->IsPrimaryFrameOfRootOrBodyElement()) { + if (auto* canvas = aFrame->PresShell()->GetCanvasFrame()) { + // Try to invalidate the canvas too, in the probable case the background + // was propagated to it. + InvalidateImages(canvas, aRequest, aForcePaint); + } + } + + bool invalidateFrame = aForcePaint; + + if (auto userDataTable = + aFrame->GetProperty(layers::WebRenderUserDataProperty::Key())) { + for (RefPtr data : userDataTable->Values()) { + switch (data->GetType()) { + case layers::WebRenderUserData::UserDataType::eFallback: + if (!IsRenderNoImages(data->GetDisplayItemKey())) { + static_cast(data.get()) + ->SetInvalid(true); + } + // XXX: handle Blob data + invalidateFrame = true; + break; + case layers::WebRenderUserData::UserDataType::eMask: + static_cast(data.get())->Invalidate(); + invalidateFrame = true; + break; + case layers::WebRenderUserData::UserDataType::eImageProvider: + if (static_cast(data.get()) + ->Invalidate(aRequest->GetProviderId())) { + break; + } + [[fallthrough]]; + default: + invalidateFrame = true; + break; + } + } + } + + // Update ancestor rendering observers (-moz-element etc) + // + // NOTE: We need to do this even if invalidateFrame is false, see bug 1114526. + { + nsIFrame* f = aFrame; + while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { + SVGObserverUtils::InvalidateDirectRenderingObservers(f); + f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f); + } + } + + if (invalidateFrame) { + aFrame->SchedulePaint(); + } +} + +void ImageLoader::UnblockOnloadIfNeeded(FrameWithFlags& aFwf) { + if (aFwf.mFlags & Flags::IsBlockingLoadEvent) { + mDocument->UnblockOnload(false); + aFwf.mFlags &= ~Flags::IsBlockingLoadEvent; + } +} + +void ImageLoader::UnblockOnloadIfNeeded(nsIFrame* aFrame, + imgIRequest* aRequest) { + MOZ_ASSERT(aFrame); + MOZ_ASSERT(aRequest); + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + size_t i = + frameSet->BinaryIndexOf(FrameWithFlags(aFrame), FrameOnlyComparator()); + if (i != FrameSet::NoIndex) { + UnblockOnloadIfNeeded(frameSet->ElementAt(i)); + } +} + +// This callback is used to unblock document onload after a reflow +// triggered from an image load. +struct ImageLoader::ImageReflowCallback final : public nsIReflowCallback { + RefPtr mLoader; + WeakFrame mFrame; + nsCOMPtr const mRequest; + + ImageReflowCallback(ImageLoader* aLoader, nsIFrame* aFrame, + imgIRequest* aRequest) + : mLoader(aLoader), mFrame(aFrame), mRequest(aRequest) {} + + bool ReflowFinished() override; + void ReflowCallbackCanceled() override; +}; + +bool ImageLoader::ImageReflowCallback::ReflowFinished() { + // Check that the frame is still valid. If it isn't, then onload was + // unblocked when the frame was removed from the FrameSet in + // RemoveRequestToFrameMapping. + if (mFrame.IsAlive()) { + mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); + } + + // Get rid of this callback object. + delete this; + + // We don't need to trigger layout. + return false; +} + +void ImageLoader::ImageReflowCallback::ReflowCallbackCanceled() { + // Check that the frame is still valid. If it isn't, then onload was + // unblocked when the frame was removed from the FrameSet in + // RemoveRequestToFrameMapping. + if (mFrame.IsAlive()) { + mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); + } + + // Get rid of this callback object. + delete this; +} + +void GlobalImageObserver::Notify(imgIRequest* aRequest, int32_t aType, + const nsIntRect* aData) { + auto entry = sImages->Lookup(aRequest); + MOZ_DIAGNOSTIC_ASSERT(entry); + if (MOZ_UNLIKELY(!entry)) { + return; + } + + const auto loadersToNotify = + ToTArray>>(entry.Data()->mImageLoaders); + for (const auto& loader : loadersToNotify) { + loader->Notify(aRequest, aType, aData); + } +} + +void ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, + const nsIntRect* aData) { + nsCString uriString; + if (profiler_is_active()) { + nsCOMPtr uri; + aRequest->GetFinalURI(getter_AddRefs(uri)); + if (uri) { + uri->GetSpec(uriString); + } + } + + AUTO_PROFILER_LABEL_DYNAMIC_CSTR("ImageLoader::Notify", OTHER, + uriString.get()); + + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { + nsCOMPtr image; + aRequest->GetImage(getter_AddRefs(image)); + return OnSizeAvailable(aRequest, image); + } + + if (aType == imgINotificationObserver::IS_ANIMATED) { + return OnImageIsAnimated(aRequest); + } + + if (aType == imgINotificationObserver::FRAME_COMPLETE) { + return OnFrameComplete(aRequest); + } + + if (aType == imgINotificationObserver::FRAME_UPDATE) { + return OnFrameUpdate(aRequest); + } + + if (aType == imgINotificationObserver::DECODE_COMPLETE) { + nsCOMPtr image; + aRequest->GetImage(getter_AddRefs(image)); + if (image && mDocument) { + image->PropagateUseCounters(mDocument); + } + } + + if (aType == imgINotificationObserver::LOAD_COMPLETE) { + return OnLoadComplete(aRequest); + } +} + +void ImageLoader::OnSizeAvailable(imgIRequest* aRequest, + imgIContainer* aImage) { + nsPresContext* presContext = GetPresContext(); + if (!presContext) { + return; + } + + aImage->SetAnimationMode(presContext->ImageAnimationMode()); + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + for (const FrameWithFlags& fwf : *frameSet) { + if (fwf.mFlags & Flags::RequiresReflowOnSizeAvailable) { + fwf.mFrame->PresShell()->FrameNeedsReflow( + fwf.mFrame, IntrinsicDirty::FrameAncestorsAndDescendants, + NS_FRAME_IS_DIRTY); + } + } +} + +void ImageLoader::OnImageIsAnimated(imgIRequest* aRequest) { + if (!mDocument) { + return; + } + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + // Register with the refresh driver now that we are aware that + // we are animated. + nsPresContext* presContext = GetPresContext(); + if (presContext) { + nsLayoutUtils::RegisterImageRequest(presContext, aRequest, nullptr); + } +} + +void ImageLoader::OnFrameComplete(imgIRequest* aRequest) { + ImageFrameChanged(aRequest, /* aFirstFrame = */ true); +} + +void ImageLoader::OnFrameUpdate(imgIRequest* aRequest) { + ImageFrameChanged(aRequest, /* aFirstFrame = */ false); +} + +void ImageLoader::ImageFrameChanged(imgIRequest* aRequest, bool aFirstFrame) { + if (!mDocument) { + return; + } + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + for (FrameWithFlags& fwf : *frameSet) { + // Since we just finished decoding a frame, we always want to paint, in + // case we're now able to paint an image that we couldn't paint before + // (and hence that we don't have retained data for). + const bool forceRepaint = aFirstFrame; + InvalidateImages(fwf.mFrame, aRequest, forceRepaint); + if (!aFirstFrame) { + // We don't reflow / try to unblock onload for subsequent frame updates. + continue; + } + if (fwf.mFlags & + Flags::RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking) { + // Tell the container of the frame to reflow because the image request + // has finished decoding its first frame. + // FIXME(emilio): Why requesting reflow on the _parent_? + nsIFrame* parent = fwf.mFrame->GetInFlowParent(); + parent->PresShell()->FrameNeedsReflow( + parent, IntrinsicDirty::FrameAncestorsAndDescendants, + NS_FRAME_IS_DIRTY); + // If we need to also potentially unblock onload, do it once reflow is + // done, with a reflow callback. + if (fwf.mFlags & Flags::IsBlockingLoadEvent) { + auto* unblocker = new ImageReflowCallback(this, fwf.mFrame, aRequest); + parent->PresShell()->PostReflowCallback(unblocker); + } + } + } +} + +void ImageLoader::OnLoadComplete(imgIRequest* aRequest) { + if (!mDocument) { + return; + } + + uint32_t status = 0; + if (NS_FAILED(aRequest->GetImageStatus(&status))) { + return; + } + + FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); + if (!frameSet) { + return; + } + + for (FrameWithFlags& fwf : *frameSet) { + if (status & imgIRequest::STATUS_ERROR) { + // Check if aRequest has an error state. If it does, we need to unblock + // Document onload for all the frames associated with this request that + // have blocked onload. This is what happens in a CORS mode violation, and + // may happen during other network events. + UnblockOnloadIfNeeded(fwf); + } + nsIFrame* frame = fwf.mFrame; + if (frame->StyleVisibility()->IsVisible()) { + frame->SchedulePaint(); + } + + if (StaticPrefs::dom_enable_largest_contentful_paint()) { + LargestContentfulPaint::MaybeProcessImageForElementTiming( + static_cast(aRequest), + frame->GetContent()->AsElement()); + } + } +} +} // namespace mozilla::css diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h new file mode 100644 index 0000000000..7b8b5338c4 --- /dev/null +++ b/layout/style/ImageLoader.h @@ -0,0 +1,170 @@ +/* -*- 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 class that handles style system image loads (other image loads are handled +// by the nodes in the content tree). + +#ifndef mozilla_css_ImageLoader_h___ +#define mozilla_css_ImageLoader_h___ + +#include "mozilla/CORSMode.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsRect.h" +#include "nsTArray.h" +#include "mozilla/Attributes.h" + +class nsIFrame; +class imgIContainer; +class imgIRequest; +class imgRequestProxy; +class nsPresContext; +class nsIURI; +class nsIPrincipal; +class nsIRequest; + +namespace mozilla { +struct MediaFeatureChange; +struct StyleComputedUrl; +namespace dom { +class Document; +} + +namespace css { + +/** + * NOTE: All methods must be called from the main thread unless otherwise + * specified. + */ +class ImageLoader final { + public: + static void Init(); + static void Shutdown(); + + // We also associate flags alongside frames in the request-to-frames hashmap. + // These are used for special handling of events for requests. + enum class Flags : uint32_t { + // Used for bullets. + RequiresReflowOnSizeAvailable = 1u << 0, + + // Used for shapes. + RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking = 1u << 1, + + // Internal flag, shouldn't be used by callers. + IsBlockingLoadEvent = 1u << 2, + }; + + explicit ImageLoader(dom::Document* aDocument) : mDocument(aDocument) { + MOZ_ASSERT(mDocument); + } + + NS_INLINE_DECL_REFCOUNTING(ImageLoader) + + void DropDocumentReference(); + + void AssociateRequestToFrame(imgIRequest*, nsIFrame*, Flags = Flags(0)); + void DisassociateRequestFromFrame(imgIRequest*, nsIFrame*); + void DropRequestsForFrame(nsIFrame*); + + void SetAnimationMode(uint16_t aMode); + + // The prescontext for this ImageLoader's document. We need it to be passed + // in because this can be called during presentation destruction after the + // presshell pointer on the document has been cleared. + void ClearFrames(nsPresContext* aPresContext); + + // Triggers an image load. + static already_AddRefed LoadImage(const StyleComputedUrl&, + dom::Document&); + + // Usually, only one style value owns a given proxy. However, we have a hack + // to share image proxies in chrome documents under some circumstances. We + // need to keep track of this so that we don't stop tracking images too early. + // + // In practice it shouldn't matter as these chrome images are mostly static, + // but it is always good to keep sanity. + static void NoteSharedLoad(imgRequestProxy*); + + // Undoes what `LoadImage` does. + static void UnloadImage(imgRequestProxy*); + + // This is called whenever an image we care about notifies the + // GlobalImageObserver. + void Notify(imgIRequest*, int32_t aType, const nsIntRect* aData); + + private: + // Called when we stop caring about a given request. + void DeregisterImageRequest(imgIRequest*, nsPresContext*); + struct ImageReflowCallback; + + ~ImageLoader() = default; + + // We need to be able to look up the frames associated with a request (for + // delivering notifications) and the requests associated with a frame (when + // the frame goes away). Thus we maintain hashtables going both ways. These + // should always be in sync. + + struct FrameWithFlags { + explicit FrameWithFlags(nsIFrame* aFrame) : mFrame(aFrame) { + MOZ_ASSERT(mFrame); + } + nsIFrame* const mFrame; + Flags mFlags{0}; + }; + + // A helper class to compare FrameWithFlags by comparing mFrame and + // ignoring mFlags. + class FrameOnlyComparator { + public: + bool Equals(const FrameWithFlags& aElem1, + const FrameWithFlags& aElem2) const { + return aElem1.mFrame == aElem2.mFrame; + } + + bool LessThan(const FrameWithFlags& aElem1, + const FrameWithFlags& aElem2) const { + return aElem1.mFrame < aElem2.mFrame; + } + }; + + typedef nsTArray FrameSet; + typedef nsTArray> RequestSet; + typedef nsClassHashtable RequestToFrameMap; + typedef nsClassHashtable, RequestSet> + FrameToRequestMap; + + nsPresContext* GetPresContext(); + + void ImageFrameChanged(imgIRequest*, bool aFirstFrame); + void UnblockOnloadIfNeeded(nsIFrame*, imgIRequest*); + void UnblockOnloadIfNeeded(FrameWithFlags&); + + void OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage); + void OnFrameComplete(imgIRequest* aRequest); + void OnImageIsAnimated(imgIRequest* aRequest); + void OnFrameUpdate(imgIRequest* aRequest); + void OnLoadComplete(imgIRequest* aRequest); + + // Helpers for DropRequestsForFrame / DisassociateRequestFromFrame above. + void RemoveRequestToFrameMapping(imgIRequest* aRequest, nsIFrame* aFrame); + void RemoveFrameToRequestMapping(imgIRequest* aRequest, nsIFrame* aFrame); + + // A map of imgIRequests to the nsIFrames that are using them. + RequestToFrameMap mRequestToFrameMap; + + // A map of nsIFrames to the imgIRequests they use. + FrameToRequestMap mFrameToRequestMap; + + // A weak pointer to our document. Nulled out by DropDocumentReference. + dom::Document* mDocument; +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ImageLoader::Flags) + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_ImageLoader_h___ */ diff --git a/layout/style/ImportScanner.cpp b/layout/style/ImportScanner.cpp new file mode 100644 index 0000000000..22d6c554f4 --- /dev/null +++ b/layout/style/ImportScanner.cpp @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* 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 "ImportScanner.h" + +#include "mozilla/ServoBindings.h" +#include "mozilla/StaticPrefs_layout.h" +#include "nsContentUtils.h" + +namespace mozilla { + +static inline bool IsWhitespace(char16_t aChar) { + return nsContentUtils::IsHTMLWhitespace(aChar); +} + +static inline bool OptionalSupportsMatches(const nsAString& aAfterRuleValue) { + // Ensure pref for @import supports() is enabled before wanting to check. + if (!StaticPrefs::layout_css_import_supports_enabled()) { + return true; + } + + // Empty, don't bother checking. + if (aAfterRuleValue.IsEmpty()) { + return true; + } + + NS_ConvertUTF16toUTF8 value(aAfterRuleValue); + return Servo_CSSSupportsForImport(&value); +} + +void ImportScanner::ResetState() { + mInImportRule = false; + // We try to avoid freeing the buffers here. + mRuleName.Truncate(0); + mRuleValue.Truncate(0); + mAfterRuleValue.Truncate(0); +} + +void ImportScanner::Start() { + Stop(); + mState = State::Idle; +} + +void ImportScanner::EmitUrl() { + MOZ_ASSERT(mState == State::AfterRuleValue); + if (mInImportRule) { + // Trim trailing whitespace from an unquoted URL. + if (mUrlValueDelimiterClosingChar == ')') { + // FIXME: Add a convenience function in nsContentUtils or something? + mRuleValue.Trim(" \t\n\r\f", false); + } + + // If a supports(...) condition is given as part of import conditions, + // only emit the URL if it matches, as there is no use preloading + // imports for features we do not support, as this cannot change + // mid-page. + if (OptionalSupportsMatches(mAfterRuleValue)) { + mUrlsFound.AppendElement(std::move(mRuleValue)); + } + } + ResetState(); + MOZ_ASSERT(mRuleValue.IsEmpty()); +} + +nsTArray ImportScanner::Stop() { + if (mState == State::AfterRuleValue) { + EmitUrl(); + } + mState = State::OutsideOfStyleElement; + ResetState(); + return std::move(mUrlsFound); +} + +nsTArray ImportScanner::Scan(Span aFragment) { + MOZ_ASSERT(ShouldScan()); + + for (char16_t c : aFragment) { + mState = Scan(c); + if (mState == State::Done) { + break; + } + } + + return std::move(mUrlsFound); +} + +auto ImportScanner::Scan(char16_t aChar) -> State { + switch (mState) { + case State::OutsideOfStyleElement: + case State::Done: + MOZ_ASSERT_UNREACHABLE("How?"); + return mState; + case State::Idle: { + // TODO(emilio): Maybe worth caring about html-style comments like: + // + if (IsWhitespace(aChar)) { + return mState; + } + if (aChar == '/') { + return State::MaybeAtCommentStart; + } + if (aChar == '@') { + MOZ_ASSERT(mRuleName.IsEmpty()); + return State::AtRuleName; + } + return State::Done; + } + case State::MaybeAtCommentStart: { + return aChar == '*' ? State::AtComment : State::Done; + } + case State::AtComment: { + return aChar == '*' ? State::MaybeAtCommentEnd : mState; + } + case State::MaybeAtCommentEnd: { + return aChar == '/' ? State::Idle : State::AtComment; + } + case State::AtRuleName: { + if (IsAsciiAlpha(aChar)) { + if (mRuleName.Length() > kMaxRuleNameLength - 1) { + return State::Done; + } + mRuleName.Append(aChar); + return mState; + } + if (IsWhitespace(aChar)) { + mInImportRule = mRuleName.LowerCaseEqualsLiteral("import"); + if (mInImportRule) { + return State::AtRuleValue; + } + // Ignorable rules, we skip until the next semi-colon for these. + if (mRuleName.LowerCaseEqualsLiteral("charset") || + mRuleName.LowerCaseEqualsLiteral("layer")) { + MOZ_ASSERT(mRuleValue.IsEmpty()); + return State::AfterRuleValue; + } + } + return State::Done; + } + case State::AtRuleValue: { + MOZ_ASSERT(mInImportRule, "Should only get to this state for @import"); + if (mRuleValue.IsEmpty()) { + if (IsWhitespace(aChar)) { + return mState; + } + if (aChar == '"' || aChar == '\'') { + mUrlValueDelimiterClosingChar = aChar; + return State::AtRuleValueDelimited; + } + if (aChar == 'u' || aChar == 'U') { + mRuleValue.Append('u'); + return mState; + } + return State::Done; + } + if (mRuleValue.Length() == 1) { + MOZ_ASSERT(mRuleValue.EqualsLiteral("u")); + if (aChar == 'r' || aChar == 'R') { + mRuleValue.Append('r'); + return mState; + } + return State::Done; + } + if (mRuleValue.Length() == 2) { + MOZ_ASSERT(mRuleValue.EqualsLiteral("ur")); + if (aChar == 'l' || aChar == 'L') { + mRuleValue.Append('l'); + } + return mState; + } + if (mRuleValue.Length() == 3) { + MOZ_ASSERT(mRuleValue.EqualsLiteral("url")); + if (aChar == '(') { + mUrlValueDelimiterClosingChar = ')'; + mRuleValue.Truncate(0); + return State::AtRuleValueDelimited; + } + return State::Done; + } + MOZ_ASSERT_UNREACHABLE( + "How? We should find a paren or a string delimiter"); + return State::Done; + } + case State::AtRuleValueDelimited: { + MOZ_ASSERT(mInImportRule, "Should only get to this state for @import"); + if (aChar == mUrlValueDelimiterClosingChar) { + return State::AfterRuleValue; + } + if (mUrlValueDelimiterClosingChar == ')' && mRuleValue.IsEmpty()) { + if (IsWhitespace(aChar)) { + return mState; + } + if (aChar == '"' || aChar == '\'') { + // Handle url("") and url(''). + mUrlValueDelimiterClosingChar = aChar; + return mState; + } + } + if (!mRuleValue.Append(aChar, mozilla::fallible)) { + mRuleValue.Truncate(0); + return State::Done; + } + return mState; + } + case State::AfterRuleValue: { + if (aChar == ';') { + EmitUrl(); + return State::Idle; + } + // If there's a selector here and the import was unterminated, just give + // up. + if (aChar == '{') { + return State::Done; + } + + if (!mAfterRuleValue.Append(aChar, mozilla::fallible)) { + mAfterRuleValue.Truncate(0); + return State::Done; + } + + return mState; // There can be all sorts of stuff here like media + // queries or what not. + } + } + MOZ_ASSERT_UNREACHABLE("Forgot to handle a state?"); + return State::Done; +} + +} // namespace mozilla diff --git a/layout/style/ImportScanner.h b/layout/style/ImportScanner.h new file mode 100644 index 0000000000..ff1755fb8b --- /dev/null +++ b/layout/style/ImportScanner.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78: */ +/* 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_ImportScanner_h +#define mozilla_ImportScanner_h + +/* A simple best-effort scanner for @import rules for the HTML parser */ + +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { + +struct ImportScanner final { + ImportScanner() = default; + + // Called when a + + + + +
+ +
+
+ + + + +
+ + diff --git a/layout/style/crashtests/1066089-1.html b/layout/style/crashtests/1066089-1.html new file mode 100644 index 0000000000..019a20a7f8 --- /dev/null +++ b/layout/style/crashtests/1066089-1.html @@ -0,0 +1,21 @@ + + + + + + + + +
  • Don't technically need any text here, but here's some anyway. diff --git a/layout/style/crashtests/1074651-1.html b/layout/style/crashtests/1074651-1.html new file mode 100644 index 0000000000..b76855cc1d --- /dev/null +++ b/layout/style/crashtests/1074651-1.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/crashtests/1089463-1.html b/layout/style/crashtests/1089463-1.html new file mode 100644 index 0000000000..8efb3f6674 --- /dev/null +++ b/layout/style/crashtests/1089463-1.html @@ -0,0 +1,20 @@ + +
    + diff --git a/layout/style/crashtests/1135534.html b/layout/style/crashtests/1135534.html new file mode 100644 index 0000000000..920ec9c7bf --- /dev/null +++ b/layout/style/crashtests/1135534.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/1136010-1.html b/layout/style/crashtests/1136010-1.html new file mode 100644 index 0000000000..bdf63f9c08 --- /dev/null +++ b/layout/style/crashtests/1136010-1.html @@ -0,0 +1,16 @@ + + +
    xy
    + diff --git a/layout/style/crashtests/1146101-1.html b/layout/style/crashtests/1146101-1.html new file mode 100644 index 0000000000..e3f8f2aa3f --- /dev/null +++ b/layout/style/crashtests/1146101-1.html @@ -0,0 +1,10 @@ + + + +
    diff --git a/layout/style/crashtests/1153693-1.html b/layout/style/crashtests/1153693-1.html new file mode 100644 index 0000000000..8035f1b218 --- /dev/null +++ b/layout/style/crashtests/1153693-1.html @@ -0,0 +1,22 @@ + + + + + + + + +
    +
    +
    + + + + diff --git a/layout/style/crashtests/1156969.svg b/layout/style/crashtests/1156969.svg new file mode 100644 index 0000000000..723fec12d1 --- /dev/null +++ b/layout/style/crashtests/1156969.svg @@ -0,0 +1,8 @@ + + + + diff --git a/layout/style/crashtests/1161320-1.html b/layout/style/crashtests/1161320-1.html new file mode 100644 index 0000000000..3cb3a7b45e --- /dev/null +++ b/layout/style/crashtests/1161320-1.html @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/layout/style/crashtests/1161320-2.html b/layout/style/crashtests/1161320-2.html new file mode 100644 index 0000000000..71db694d01 --- /dev/null +++ b/layout/style/crashtests/1161320-2.html @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/layout/style/crashtests/1161366-1.html b/layout/style/crashtests/1161366-1.html new file mode 100644 index 0000000000..d4eacccdc9 --- /dev/null +++ b/layout/style/crashtests/1161366-1.html @@ -0,0 +1,7 @@ + diff --git a/layout/style/crashtests/1163446-1.html b/layout/style/crashtests/1163446-1.html new file mode 100644 index 0000000000..a3ca0c44d5 --- /dev/null +++ b/layout/style/crashtests/1163446-1.html @@ -0,0 +1,4 @@ + diff --git a/layout/style/crashtests/1164813-1.html b/layout/style/crashtests/1164813-1.html new file mode 100644 index 0000000000..ee5a60ffd6 --- /dev/null +++ b/layout/style/crashtests/1164813-1.html @@ -0,0 +1,33 @@ + + + +
    +
    Searching
    +
    + + diff --git a/layout/style/crashtests/1167782-1.html b/layout/style/crashtests/1167782-1.html new file mode 100644 index 0000000000..4b41ea3983 --- /dev/null +++ b/layout/style/crashtests/1167782-1.html @@ -0,0 +1,11 @@ + + + + + + + diff --git a/layout/style/crashtests/1186768-1.xhtml b/layout/style/crashtests/1186768-1.xhtml new file mode 100644 index 0000000000..22608557df --- /dev/null +++ b/layout/style/crashtests/1186768-1.xhtml @@ -0,0 +1,10 @@ + + + + +
    +
    + +
    + + diff --git a/layout/style/crashtests/1200568-1.html b/layout/style/crashtests/1200568-1.html new file mode 100644 index 0000000000..e2dc9c09df --- /dev/null +++ b/layout/style/crashtests/1200568-1.html @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/layout/style/crashtests/1206105-1.html b/layout/style/crashtests/1206105-1.html new file mode 100644 index 0000000000..88af39e532 --- /dev/null +++ b/layout/style/crashtests/1206105-1.html @@ -0,0 +1,6 @@ + +crashtest, bug 1206105 + + diff --git a/layout/style/crashtests/1223688-1.html b/layout/style/crashtests/1223688-1.html new file mode 100644 index 0000000000..70f9d85795 --- /dev/null +++ b/layout/style/crashtests/1223688-1.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/layout/style/crashtests/1223694-1.html b/layout/style/crashtests/1223694-1.html new file mode 100644 index 0000000000..c4589884f7 --- /dev/null +++ b/layout/style/crashtests/1223694-1.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/style/crashtests/1226400-1.html b/layout/style/crashtests/1226400-1.html new file mode 100644 index 0000000000..dea15642c7 --- /dev/null +++ b/layout/style/crashtests/1226400-1.html @@ -0,0 +1,55 @@ + + + +FontFaceSet::Load crasher + + + + + + + +

    This may crash on load...

    + + + + diff --git a/layout/style/crashtests/1227498.html b/layout/style/crashtests/1227498.html new file mode 100644 index 0000000000..3ccc263fd5 --- /dev/null +++ b/layout/style/crashtests/1227498.html @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/layout/style/crashtests/1227501-1.html b/layout/style/crashtests/1227501-1.html new file mode 100644 index 0000000000..03383813d0 --- /dev/null +++ b/layout/style/crashtests/1227501-1.html @@ -0,0 +1,8 @@ + + + + + + diff --git a/layout/style/crashtests/1228789-1.html b/layout/style/crashtests/1228789-1.html new file mode 100644 index 0000000000..b8e7ffda61 --- /dev/null +++ b/layout/style/crashtests/1228789-1.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/crashtests/1230408-1.html b/layout/style/crashtests/1230408-1.html new file mode 100644 index 0000000000..0a2588c8ae --- /dev/null +++ b/layout/style/crashtests/1230408-1.html @@ -0,0 +1,8 @@ + + + +C + diff --git a/layout/style/crashtests/1233135-1.html b/layout/style/crashtests/1233135-1.html new file mode 100644 index 0000000000..58a82f3049 --- /dev/null +++ b/layout/style/crashtests/1233135-1.html @@ -0,0 +1,13 @@ + + +
    + + + + + + diff --git a/layout/style/crashtests/1233135-2.html b/layout/style/crashtests/1233135-2.html new file mode 100644 index 0000000000..03e356ae1f --- /dev/null +++ b/layout/style/crashtests/1233135-2.html @@ -0,0 +1,11 @@ + + +
    + + + + + + + +
    diff --git a/layout/style/crashtests/1236398.xhtml b/layout/style/crashtests/1236398.xhtml new file mode 100644 index 0000000000..c1739fac72 --- /dev/null +++ b/layout/style/crashtests/1236398.xhtml @@ -0,0 +1,71 @@ + ]> + + + + + Crashing Firefox 3.0.2 + + + + + + + + + + +
    +
    +
    + + + +
    Text
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    [x???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????]
    +
    +
    .
    +
    + +
    +
    +
    + + diff --git a/layout/style/crashtests/1238660-1.html b/layout/style/crashtests/1238660-1.html new file mode 100644 index 0000000000..2323d2cfa5 --- /dev/null +++ b/layout/style/crashtests/1238660-1.html @@ -0,0 +1,19 @@ + + + + Bug 1238660 + + + +
    + +
    +
    + + + diff --git a/layout/style/crashtests/1245260-1.html b/layout/style/crashtests/1245260-1.html new file mode 100644 index 0000000000..6f2dda9955 --- /dev/null +++ b/layout/style/crashtests/1245260-1.html @@ -0,0 +1,53 @@ + + + + +Bug 1245260 + + + + + + + + diff --git a/layout/style/crashtests/1250791.html b/layout/style/crashtests/1250791.html new file mode 100644 index 0000000000..8fb0a16431 --- /dev/null +++ b/layout/style/crashtests/1250791.html @@ -0,0 +1,8 @@ + + + +
    +
    +
    + + diff --git a/layout/style/crashtests/1264396-1.html b/layout/style/crashtests/1264396-1.html new file mode 100644 index 0000000000..abba4de3b5 --- /dev/null +++ b/layout/style/crashtests/1264396-1.html @@ -0,0 +1,14 @@ + + + + + diff --git a/layout/style/crashtests/1264949.html b/layout/style/crashtests/1264949.html new file mode 100644 index 0000000000..780bff97a3 --- /dev/null +++ b/layout/style/crashtests/1264949.html @@ -0,0 +1,23 @@ + + + + + + +
    + + ALongLongLongLongLongLongLongLongLongLongLongLongString + +
    + + diff --git a/layout/style/crashtests/1265611-1.html b/layout/style/crashtests/1265611-1.html new file mode 100644 index 0000000000..df5467ffa3 --- /dev/null +++ b/layout/style/crashtests/1265611-1.html @@ -0,0 +1,24 @@ + + + + diff --git a/layout/style/crashtests/1270795.html b/layout/style/crashtests/1270795.html new file mode 100644 index 0000000000..c4262078ed --- /dev/null +++ b/layout/style/crashtests/1270795.html @@ -0,0 +1,15 @@ + + + + + + + +
    Table 1
    + + + + +
    Table 2
    + + \ No newline at end of file diff --git a/layout/style/crashtests/1275026.html b/layout/style/crashtests/1275026.html new file mode 100644 index 0000000000..7960d2889a --- /dev/null +++ b/layout/style/crashtests/1275026.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/layout/style/crashtests/1277908-1.html b/layout/style/crashtests/1277908-1.html new file mode 100644 index 0000000000..c8dae69068 --- /dev/null +++ b/layout/style/crashtests/1277908-1.html @@ -0,0 +1,26 @@ + diff --git a/layout/style/crashtests/1277908-2.html b/layout/style/crashtests/1277908-2.html new file mode 100644 index 0000000000..4c5266826a --- /dev/null +++ b/layout/style/crashtests/1277908-2.html @@ -0,0 +1,19 @@ + + diff --git a/layout/style/crashtests/1278463-1.html b/layout/style/crashtests/1278463-1.html new file mode 100644 index 0000000000..da3b976d88 --- /dev/null +++ b/layout/style/crashtests/1278463-1.html @@ -0,0 +1,21 @@ + + + + + + +
    + + diff --git a/layout/style/crashtests/1279819-1.html b/layout/style/crashtests/1279819-1.html new file mode 100644 index 0000000000..1794551739 --- /dev/null +++ b/layout/style/crashtests/1279819-1.html @@ -0,0 +1,18 @@ + + + diff --git a/layout/style/crashtests/1282076-1.html b/layout/style/crashtests/1282076-1.html new file mode 100644 index 0000000000..d5d1f0c74b --- /dev/null +++ b/layout/style/crashtests/1282076-1.html @@ -0,0 +1,51 @@ + + diff --git a/layout/style/crashtests/1282076-2.html b/layout/style/crashtests/1282076-2.html new file mode 100644 index 0000000000..1c6f986396 --- /dev/null +++ b/layout/style/crashtests/1282076-2.html @@ -0,0 +1,46 @@ + + diff --git a/layout/style/crashtests/1290994-1.html b/layout/style/crashtests/1290994-1.html new file mode 100644 index 0000000000..d9d99a5b06 --- /dev/null +++ b/layout/style/crashtests/1290994-1.html @@ -0,0 +1,11 @@ + + + + diff --git a/layout/style/crashtests/1290994-2.html b/layout/style/crashtests/1290994-2.html new file mode 100644 index 0000000000..e592b84eee --- /dev/null +++ b/layout/style/crashtests/1290994-2.html @@ -0,0 +1,11 @@ + + + + diff --git a/layout/style/crashtests/1290994-3.html b/layout/style/crashtests/1290994-3.html new file mode 100644 index 0000000000..76589f5a69 --- /dev/null +++ b/layout/style/crashtests/1290994-3.html @@ -0,0 +1,11 @@ + + + + diff --git a/layout/style/crashtests/1290994-4.html b/layout/style/crashtests/1290994-4.html new file mode 100644 index 0000000000..4278856d0c --- /dev/null +++ b/layout/style/crashtests/1290994-4.html @@ -0,0 +1,8 @@ + + + + diff --git a/layout/style/crashtests/1314531.html b/layout/style/crashtests/1314531.html new file mode 100644 index 0000000000..8e804643fd --- /dev/null +++ b/layout/style/crashtests/1314531.html @@ -0,0 +1,2 @@ + + diff --git a/layout/style/crashtests/1315889-1.html b/layout/style/crashtests/1315889-1.html new file mode 100644 index 0000000000..29186fac1c --- /dev/null +++ b/layout/style/crashtests/1315889-1.html @@ -0,0 +1,12 @@ + + +
    hello
    + diff --git a/layout/style/crashtests/1315894-1.html b/layout/style/crashtests/1315894-1.html new file mode 100644 index 0000000000..e0192460ff --- /dev/null +++ b/layout/style/crashtests/1315894-1.html @@ -0,0 +1,9 @@ + +
    x
    + diff --git a/layout/style/crashtests/1319072-1.html b/layout/style/crashtests/1319072-1.html new file mode 100644 index 0000000000..f6c1330559 --- /dev/null +++ b/layout/style/crashtests/1319072-1.html @@ -0,0 +1,20 @@ + + +Interpolation of decomposed matrices + +
    + diff --git a/layout/style/crashtests/1320423-1.html b/layout/style/crashtests/1320423-1.html new file mode 100644 index 0000000000..9769455e75 --- /dev/null +++ b/layout/style/crashtests/1320423-1.html @@ -0,0 +1,22 @@ + + + +
    + diff --git a/layout/style/crashtests/1321357-1.html b/layout/style/crashtests/1321357-1.html new file mode 100644 index 0000000000..7b3a3c39c0 --- /dev/null +++ b/layout/style/crashtests/1321357-1.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/layout/style/crashtests/1328535-1.html b/layout/style/crashtests/1328535-1.html new file mode 100644 index 0000000000..3738380978 --- /dev/null +++ b/layout/style/crashtests/1328535-1.html @@ -0,0 +1,17 @@ + + + +
    diff --git a/layout/style/crashtests/1331272.html b/layout/style/crashtests/1331272.html new file mode 100644 index 0000000000..6ee9f022f7 --- /dev/null +++ b/layout/style/crashtests/1331272.html @@ -0,0 +1,16 @@ + + +
    + diff --git a/layout/style/crashtests/1332550.html b/layout/style/crashtests/1332550.html new file mode 100644 index 0000000000..4299450a58 --- /dev/null +++ b/layout/style/crashtests/1332550.html @@ -0,0 +1,19 @@ + + + + diff --git a/layout/style/crashtests/1333001-1.css b/layout/style/crashtests/1333001-1.css new file mode 100644 index 0000000000..ee2d477b79 --- /dev/null +++ b/layout/style/crashtests/1333001-1.css @@ -0,0 +1 @@ +@import url(chrome://foo); diff --git a/layout/style/crashtests/1333001-1.html b/layout/style/crashtests/1333001-1.html new file mode 100644 index 0000000000..2fb577b7bd --- /dev/null +++ b/layout/style/crashtests/1333001-1.html @@ -0,0 +1,9 @@ + + + + + diff --git a/layout/style/crashtests/1340248.html b/layout/style/crashtests/1340248.html new file mode 100644 index 0000000000..04e7ebaada --- /dev/null +++ b/layout/style/crashtests/1340248.html @@ -0,0 +1,14 @@ + + + + + + diff --git a/layout/style/crashtests/1340344.html b/layout/style/crashtests/1340344.html new file mode 100644 index 0000000000..5d0a3c85fd --- /dev/null +++ b/layout/style/crashtests/1340344.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/layout/style/crashtests/1342316-1.html b/layout/style/crashtests/1342316-1.html new file mode 100644 index 0000000000..091c6e8261 --- /dev/null +++ b/layout/style/crashtests/1342316-1.html @@ -0,0 +1,20 @@ + + +calc() in translate3d as base style of transform animation + +
    + diff --git a/layout/style/crashtests/1344210.html b/layout/style/crashtests/1344210.html new file mode 100644 index 0000000000..f6b39eb082 --- /dev/null +++ b/layout/style/crashtests/1344210.html @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/1353312.html b/layout/style/crashtests/1353312.html new file mode 100644 index 0000000000..56adf9ed6d --- /dev/null +++ b/layout/style/crashtests/1353312.html @@ -0,0 +1,14 @@ + + + +
    x
    +
    x
    +
    x
    diff --git a/layout/style/crashtests/1356601-1.html b/layout/style/crashtests/1356601-1.html new file mode 100644 index 0000000000..4fc28bd58f --- /dev/null +++ b/layout/style/crashtests/1356601-1.html @@ -0,0 +1,18 @@ + + + +
    + Crash +
    + diff --git a/layout/style/crashtests/1364139-1.html b/layout/style/crashtests/1364139-1.html new file mode 100644 index 0000000000..d33093d018 --- /dev/null +++ b/layout/style/crashtests/1364139-1.html @@ -0,0 +1,20 @@ + + + + + diff --git a/layout/style/crashtests/1371450-1.html b/layout/style/crashtests/1371450-1.html new file mode 100644 index 0000000000..199f326bb5 --- /dev/null +++ b/layout/style/crashtests/1371450-1.html @@ -0,0 +1,34 @@ + + + + +
    text
    +
    animation
    + diff --git a/layout/style/crashtests/1374175-1.html b/layout/style/crashtests/1374175-1.html new file mode 100644 index 0000000000..e534f77719 --- /dev/null +++ b/layout/style/crashtests/1374175-1.html @@ -0,0 +1,12 @@ + + diff --git a/layout/style/crashtests/1375812-1.html b/layout/style/crashtests/1375812-1.html new file mode 100644 index 0000000000..e51fc60faf --- /dev/null +++ b/layout/style/crashtests/1375812-1.html @@ -0,0 +1,25 @@ + + + +Bug 1375812 - Interpolation between interpolatematrix and none tranform + + + + + + +
    + + diff --git a/layout/style/crashtests/1377053-1.html b/layout/style/crashtests/1377053-1.html new file mode 100644 index 0000000000..829a36128d --- /dev/null +++ b/layout/style/crashtests/1377053-1.html @@ -0,0 +1,10 @@ + + +
    diff --git a/layout/style/crashtests/1377256-1-helper.html b/layout/style/crashtests/1377256-1-helper.html new file mode 100644 index 0000000000..67c7dd05b4 --- /dev/null +++ b/layout/style/crashtests/1377256-1-helper.html @@ -0,0 +1,13 @@ + + + +> + + + diff --git a/layout/style/crashtests/559491.html b/layout/style/crashtests/559491.html new file mode 100644 index 0000000000..19126e5603 --- /dev/null +++ b/layout/style/crashtests/559491.html @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/layout/style/crashtests/565248-1.html b/layout/style/crashtests/565248-1.html new file mode 100644 index 0000000000..18c8add08e --- /dev/null +++ b/layout/style/crashtests/565248-1.html @@ -0,0 +1,2 @@ + + diff --git a/layout/style/crashtests/571105-1.xhtml b/layout/style/crashtests/571105-1.xhtml new file mode 100644 index 0000000000..4dce42dc8e --- /dev/null +++ b/layout/style/crashtests/571105-1.xhtml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layout/style/crashtests/573127-1.html b/layout/style/crashtests/573127-1.html new file mode 100644 index 0000000000..72fbaf63ef --- /dev/null +++ b/layout/style/crashtests/573127-1.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/layout/style/crashtests/575464-1.html b/layout/style/crashtests/575464-1.html new file mode 100644 index 0000000000..e77649ed74 --- /dev/null +++ b/layout/style/crashtests/575464-1.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/580685.html b/layout/style/crashtests/580685.html new file mode 100644 index 0000000000..2a9115cb60 --- /dev/null +++ b/layout/style/crashtests/580685.html @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/585185-1.html b/layout/style/crashtests/585185-1.html new file mode 100644 index 0000000000..17eec73b36 --- /dev/null +++ b/layout/style/crashtests/585185-1.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/588627-1.html b/layout/style/crashtests/588627-1.html new file mode 100644 index 0000000000..510a20dd58 --- /dev/null +++ b/layout/style/crashtests/588627-1.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/layout/style/crashtests/592698-1.html b/layout/style/crashtests/592698-1.html new file mode 100644 index 0000000000..b7570f6e4c --- /dev/null +++ b/layout/style/crashtests/592698-1.html @@ -0,0 +1,29 @@ + + + + + + diff --git a/layout/style/crashtests/601437-1.html b/layout/style/crashtests/601437-1.html new file mode 100644 index 0000000000..8b5efd6cca --- /dev/null +++ b/layout/style/crashtests/601437-1.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/layout/style/crashtests/601439-1.html b/layout/style/crashtests/601439-1.html new file mode 100644 index 0000000000..e76e4520e9 --- /dev/null +++ b/layout/style/crashtests/601439-1.html @@ -0,0 +1,8 @@ + + diff --git a/layout/style/crashtests/605689-1.html b/layout/style/crashtests/605689-1.html new file mode 100644 index 0000000000..be02ac2e71 --- /dev/null +++ b/layout/style/crashtests/605689-1.html @@ -0,0 +1,13 @@ + + + diff --git a/layout/style/crashtests/611922-1.html b/layout/style/crashtests/611922-1.html new file mode 100644 index 0000000000..f6affa0de1 --- /dev/null +++ b/layout/style/crashtests/611922-1.html @@ -0,0 +1,13 @@ + + + + This link starts out visited + + + diff --git a/layout/style/crashtests/612213.html b/layout/style/crashtests/612213.html new file mode 100644 index 0000000000..a97b21bca5 --- /dev/null +++ b/layout/style/crashtests/612213.html @@ -0,0 +1,17 @@ + + + + + + diff --git a/layout/style/crashtests/621596-1.html b/layout/style/crashtests/621596-1.html new file mode 100644 index 0000000000..95bb498bf7 --- /dev/null +++ b/layout/style/crashtests/621596-1.html @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/layout/style/crashtests/622314-1.xhtml b/layout/style/crashtests/622314-1.xhtml new file mode 100644 index 0000000000..4daf23169f --- /dev/null +++ b/layout/style/crashtests/622314-1.xhtml @@ -0,0 +1,26 @@ + +
    + + + + + + + + + + + + + + + + + + + + + + +
    + diff --git a/layout/style/crashtests/635153.html b/layout/style/crashtests/635153.html new file mode 100644 index 0000000000..50f05878a1 --- /dev/null +++ b/layout/style/crashtests/635153.html @@ -0,0 +1,16 @@ + + + + + + + diff --git a/layout/style/crashtests/637242.xhtml b/layout/style/crashtests/637242.xhtml new file mode 100644 index 0000000000..a8d99a7325 --- /dev/null +++ b/layout/style/crashtests/637242.xhtml @@ -0,0 +1,27 @@ + + + + + + +

    This should be green

    + + diff --git a/layout/style/crashtests/645142.html b/layout/style/crashtests/645142.html new file mode 100644 index 0000000000..290355c187 --- /dev/null +++ b/layout/style/crashtests/645142.html @@ -0,0 +1,11 @@ + +Testcase for bug 645142 + +
    +
    +
    +
    +
    +
    +
    + diff --git a/layout/style/crashtests/652976-1.svg b/layout/style/crashtests/652976-1.svg new file mode 100644 index 0000000000..1ca6ee28ec --- /dev/null +++ b/layout/style/crashtests/652976-1.svg @@ -0,0 +1,10 @@ + + + + diff --git a/layout/style/crashtests/653675.html b/layout/style/crashtests/653675.html new file mode 100644 index 0000000000..ad41881aac --- /dev/null +++ b/layout/style/crashtests/653675.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/665209-1.html b/layout/style/crashtests/665209-1.html new file mode 100644 index 0000000000..30e8055ebb --- /dev/null +++ b/layout/style/crashtests/665209-1.html @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/layout/style/crashtests/671799-1.html b/layout/style/crashtests/671799-1.html new file mode 100644 index 0000000000..cc89495b36 --- /dev/null +++ b/layout/style/crashtests/671799-1.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/style/crashtests/671799-2.html b/layout/style/crashtests/671799-2.html new file mode 100644 index 0000000000..a8398680e0 --- /dev/null +++ b/layout/style/crashtests/671799-2.html @@ -0,0 +1,17 @@ + + + + + + +foo bar + + diff --git a/layout/style/crashtests/690990-1.html b/layout/style/crashtests/690990-1.html new file mode 100644 index 0000000000..19520e4f90 --- /dev/null +++ b/layout/style/crashtests/690990-1.html @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/layout/style/crashtests/694775.html b/layout/style/crashtests/694775.html new file mode 100644 index 0000000000..2952d5e6e8 --- /dev/null +++ b/layout/style/crashtests/694775.html @@ -0,0 +1,7 @@ + + diff --git a/layout/style/crashtests/696188-1.html b/layout/style/crashtests/696188-1.html new file mode 100644 index 0000000000..7442743cfc --- /dev/null +++ b/layout/style/crashtests/696188-1.html @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/layout/style/crashtests/696869-1.html b/layout/style/crashtests/696869-1.html new file mode 100644 index 0000000000..b7f3dccd25 --- /dev/null +++ b/layout/style/crashtests/696869-1.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/layout/style/crashtests/700116.html b/layout/style/crashtests/700116.html new file mode 100644 index 0000000000..de35ce7029 --- /dev/null +++ b/layout/style/crashtests/700116.html @@ -0,0 +1,5 @@ + + + diff --git a/layout/style/crashtests/729126-1.html b/layout/style/crashtests/729126-1.html new file mode 100644 index 0000000000..a5c50abd0b --- /dev/null +++ b/layout/style/crashtests/729126-1.html @@ -0,0 +1,10 @@ + + + + + diff --git a/layout/style/crashtests/729126-2.html b/layout/style/crashtests/729126-2.html new file mode 100644 index 0000000000..63533ee302 --- /dev/null +++ b/layout/style/crashtests/729126-2.html @@ -0,0 +1,10 @@ + + + + + diff --git a/layout/style/crashtests/786108-1.html b/layout/style/crashtests/786108-1.html new file mode 100644 index 0000000000..2962e71177 --- /dev/null +++ b/layout/style/crashtests/786108-1.html @@ -0,0 +1,22 @@ + + + + + diff --git a/layout/style/crashtests/786108-2.html b/layout/style/crashtests/786108-2.html new file mode 100644 index 0000000000..1b2892040b --- /dev/null +++ b/layout/style/crashtests/786108-2.html @@ -0,0 +1,23 @@ + + + + + diff --git a/layout/style/crashtests/788836.html b/layout/style/crashtests/788836.html new file mode 100644 index 0000000000..938c216753 --- /dev/null +++ b/layout/style/crashtests/788836.html @@ -0,0 +1,3 @@ + + diff --git a/layout/style/crashtests/806310-1.html b/layout/style/crashtests/806310-1.html new file mode 100644 index 0000000000..505b410769 --- /dev/null +++ b/layout/style/crashtests/806310-1.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/crashtests/809762.html b/layout/style/crashtests/809762.html new file mode 100644 index 0000000000..ad06540efd --- /dev/null +++ b/layout/style/crashtests/809762.html @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/layout/style/crashtests/812824.html b/layout/style/crashtests/812824.html new file mode 100644 index 0000000000..5367b6c308 --- /dev/null +++ b/layout/style/crashtests/812824.html @@ -0,0 +1 @@ +
    diff --git a/layout/style/crashtests/822766-1.html b/layout/style/crashtests/822766-1.html new file mode 100644 index 0000000000..21c298ceb8 --- /dev/null +++ b/layout/style/crashtests/822766-1.html @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/layout/style/crashtests/822877.html b/layout/style/crashtests/822877.html new file mode 100644 index 0000000000..8dfa192e49 --- /dev/null +++ b/layout/style/crashtests/822877.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/layout/style/crashtests/827213-1.html b/layout/style/crashtests/827213-1.html new file mode 100644 index 0000000000..16cc88a20f --- /dev/null +++ b/layout/style/crashtests/827213-1.html @@ -0,0 +1,9 @@ + + + + +
    +
    + + + diff --git a/layout/style/crashtests/827220.html b/layout/style/crashtests/827220.html new file mode 100644 index 0000000000..7c1717a1b6 --- /dev/null +++ b/layout/style/crashtests/827220.html @@ -0,0 +1,5 @@ + + + + + diff --git a/layout/style/crashtests/827591-1.html b/layout/style/crashtests/827591-1.html new file mode 100644 index 0000000000..a0ab100913 --- /dev/null +++ b/layout/style/crashtests/827591-1.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/layout/style/crashtests/829817.html b/layout/style/crashtests/829817.html new file mode 100644 index 0000000000..a81fb6b79d --- /dev/null +++ b/layout/style/crashtests/829817.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/layout/style/crashtests/842134.html b/layout/style/crashtests/842134.html new file mode 100644 index 0000000000..f5bab1214b --- /dev/null +++ b/layout/style/crashtests/842134.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/861489-1.html b/layout/style/crashtests/861489-1.html new file mode 100644 index 0000000000..bb394cef5b --- /dev/null +++ b/layout/style/crashtests/861489-1.html @@ -0,0 +1,29 @@ + + + + + + + + + +
    + + diff --git a/layout/style/crashtests/862113.html b/layout/style/crashtests/862113.html new file mode 100644 index 0000000000..319132b783 --- /dev/null +++ b/layout/style/crashtests/862113.html @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/layout/style/crashtests/867487.html b/layout/style/crashtests/867487.html new file mode 100644 index 0000000000..a259690d14 --- /dev/null +++ b/layout/style/crashtests/867487.html @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/layout/style/crashtests/873222.html b/layout/style/crashtests/873222.html new file mode 100644 index 0000000000..1ffcc623ae --- /dev/null +++ b/layout/style/crashtests/873222.html @@ -0,0 +1,17 @@ + + + + + + + diff --git a/layout/style/crashtests/873260-1.html b/layout/style/crashtests/873260-1.html new file mode 100644 index 0000000000..9461ef41ad --- /dev/null +++ b/layout/style/crashtests/873260-1.html @@ -0,0 +1,16 @@ + + + + + + + diff --git a/layout/style/crashtests/873260-2.html b/layout/style/crashtests/873260-2.html new file mode 100644 index 0000000000..9634389198 --- /dev/null +++ b/layout/style/crashtests/873260-2.html @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/layout/style/crashtests/880862.html b/layout/style/crashtests/880862.html new file mode 100644 index 0000000000..d89e24d90b --- /dev/null +++ b/layout/style/crashtests/880862.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/layout/style/crashtests/894245-1.html b/layout/style/crashtests/894245-1.html new file mode 100644 index 0000000000..b04f0fae04 --- /dev/null +++ b/layout/style/crashtests/894245-1.html @@ -0,0 +1,4 @@ + +
    +
    +
    diff --git a/layout/style/crashtests/915440.html b/layout/style/crashtests/915440.html new file mode 100644 index 0000000000..f3a291f1fe --- /dev/null +++ b/layout/style/crashtests/915440.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/crashtests/927734-1.html b/layout/style/crashtests/927734-1.html new file mode 100644 index 0000000000..bd99f9b9ee --- /dev/null +++ b/layout/style/crashtests/927734-1.html @@ -0,0 +1,10 @@ + +
    diff --git a/layout/style/crashtests/1377256-1.html b/layout/style/crashtests/1377256-1.html new file mode 100644 index 0000000000..187803f6f2 --- /dev/null +++ b/layout/style/crashtests/1377256-1.html @@ -0,0 +1,9 @@ + + + + diff --git a/layout/style/crashtests/1378064-1.html b/layout/style/crashtests/1378064-1.html new file mode 100644 index 0000000000..aaf9bb3b7d --- /dev/null +++ b/layout/style/crashtests/1378064-1.html @@ -0,0 +1,38 @@ + + + + +
    +text +text +
    + diff --git a/layout/style/crashtests/1378814.html b/layout/style/crashtests/1378814.html new file mode 100644 index 0000000000..4177063408 --- /dev/null +++ b/layout/style/crashtests/1378814.html @@ -0,0 +1,4 @@ + + diff --git a/layout/style/crashtests/1380800.html b/layout/style/crashtests/1380800.html new file mode 100644 index 0000000000..a01740cd83 --- /dev/null +++ b/layout/style/crashtests/1380800.html @@ -0,0 +1,5 @@ + + + diff --git a/layout/style/crashtests/1381420-1.html b/layout/style/crashtests/1381420-1.html new file mode 100644 index 0000000000..e4d3df8d48 --- /dev/null +++ b/layout/style/crashtests/1381420-1.html @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + Summary +

    detail description

    +
    +
    + diff --git a/layout/style/crashtests/1381682.html b/layout/style/crashtests/1381682.html new file mode 100644 index 0000000000..3db2e8a4d9 --- /dev/null +++ b/layout/style/crashtests/1381682.html @@ -0,0 +1,18 @@ + + +
    +
    +
    +
    + diff --git a/layout/style/crashtests/1382672.html b/layout/style/crashtests/1382672.html new file mode 100644 index 0000000000..86526e840e --- /dev/null +++ b/layout/style/crashtests/1382672.html @@ -0,0 +1,11 @@ + + + + + + + diff --git a/layout/style/crashtests/1382710.html b/layout/style/crashtests/1382710.html new file mode 100644 index 0000000000..46c7720d79 --- /dev/null +++ b/layout/style/crashtests/1382710.html @@ -0,0 +1,8 @@ + + + + + + diff --git a/layout/style/crashtests/1383001-2.html b/layout/style/crashtests/1383001-2.html new file mode 100644 index 0000000000..5fba0184b4 --- /dev/null +++ b/layout/style/crashtests/1383001-2.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/layout/style/crashtests/1383001.html b/layout/style/crashtests/1383001.html new file mode 100644 index 0000000000..e963c847f6 --- /dev/null +++ b/layout/style/crashtests/1383001.html @@ -0,0 +1,23 @@ + + +
    +
    +
    + + diff --git a/layout/style/crashtests/1383319.html b/layout/style/crashtests/1383319.html new file mode 100644 index 0000000000..960b539383 --- /dev/null +++ b/layout/style/crashtests/1383319.html @@ -0,0 +1,18 @@ + + +
    +
    + diff --git a/layout/style/crashtests/1383493-1.html b/layout/style/crashtests/1383493-1.html new file mode 100644 index 0000000000..12a04818ad --- /dev/null +++ b/layout/style/crashtests/1383493-1.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/layout/style/crashtests/1383589-1.html b/layout/style/crashtests/1383589-1.html new file mode 100644 index 0000000000..16c4f456ad --- /dev/null +++ b/layout/style/crashtests/1383589-1.html @@ -0,0 +1,14 @@ + + + diff --git a/layout/style/crashtests/1383975.html b/layout/style/crashtests/1383975.html new file mode 100644 index 0000000000..5cf5ee7574 --- /dev/null +++ b/layout/style/crashtests/1383975.html @@ -0,0 +1,10 @@ + + + + + + + diff --git a/layout/style/crashtests/1383981-2.html b/layout/style/crashtests/1383981-2.html new file mode 100644 index 0000000000..410e5ecff4 --- /dev/null +++ b/layout/style/crashtests/1383981-2.html @@ -0,0 +1,21 @@ + + + + + diff --git a/layout/style/crashtests/1383981-3.html b/layout/style/crashtests/1383981-3.html new file mode 100644 index 0000000000..91891d3d3d --- /dev/null +++ b/layout/style/crashtests/1383981-3.html @@ -0,0 +1,36 @@ + + + + + diff --git a/layout/style/crashtests/1383981.html b/layout/style/crashtests/1383981.html new file mode 100644 index 0000000000..94bb9700a8 --- /dev/null +++ b/layout/style/crashtests/1383981.html @@ -0,0 +1,22 @@ + + + + + diff --git a/layout/style/crashtests/1384232.html b/layout/style/crashtests/1384232.html new file mode 100644 index 0000000000..df9df64e81 --- /dev/null +++ b/layout/style/crashtests/1384232.html @@ -0,0 +1,9 @@ + + + diff --git a/layout/style/crashtests/1384824-1.html b/layout/style/crashtests/1384824-1.html new file mode 100644 index 0000000000..6dcf485f0e --- /dev/null +++ b/layout/style/crashtests/1384824-1.html @@ -0,0 +1,27 @@ + + + +
    + +
    + diff --git a/layout/style/crashtests/1384824-2.html b/layout/style/crashtests/1384824-2.html new file mode 100644 index 0000000000..65e6f29fd1 --- /dev/null +++ b/layout/style/crashtests/1384824-2.html @@ -0,0 +1,31 @@ + + + +
    + +
    + diff --git a/layout/style/crashtests/1386773.html b/layout/style/crashtests/1386773.html new file mode 100644 index 0000000000..b6267ca344 --- /dev/null +++ b/layout/style/crashtests/1386773.html @@ -0,0 +1,17 @@ + + + + + + + +A + diff --git a/layout/style/crashtests/1387481-1-iframe.html b/layout/style/crashtests/1387481-1-iframe.html new file mode 100644 index 0000000000..87fde769c5 --- /dev/null +++ b/layout/style/crashtests/1387481-1-iframe.html @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/1387481-1.html b/layout/style/crashtests/1387481-1.html new file mode 100644 index 0000000000..98783d1666 --- /dev/null +++ b/layout/style/crashtests/1387481-1.html @@ -0,0 +1,13 @@ + + + + diff --git a/layout/style/crashtests/1387499.html b/layout/style/crashtests/1387499.html new file mode 100644 index 0000000000..86afa3e84f --- /dev/null +++ b/layout/style/crashtests/1387499.html @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/1388234.html b/layout/style/crashtests/1388234.html new file mode 100644 index 0000000000..db4d52a5ef --- /dev/null +++ b/layout/style/crashtests/1388234.html @@ -0,0 +1,9 @@ + + diff --git a/layout/style/crashtests/1389645.html b/layout/style/crashtests/1389645.html new file mode 100644 index 0000000000..85d3314478 --- /dev/null +++ b/layout/style/crashtests/1389645.html @@ -0,0 +1,13 @@ + + + diff --git a/layout/style/crashtests/1390726.html b/layout/style/crashtests/1390726.html new file mode 100644 index 0000000000..308c56627b --- /dev/null +++ b/layout/style/crashtests/1390726.html @@ -0,0 +1,27 @@ + + + diff --git a/layout/style/crashtests/1391577.html b/layout/style/crashtests/1391577.html new file mode 100644 index 0000000000..b0e3211c65 --- /dev/null +++ b/layout/style/crashtests/1391577.html @@ -0,0 +1,14 @@ + + +
    + +
    + diff --git a/layout/style/crashtests/1393189.html b/layout/style/crashtests/1393189.html new file mode 100644 index 0000000000..5a0bfc7244 --- /dev/null +++ b/layout/style/crashtests/1393189.html @@ -0,0 +1,13 @@ + + +
    diff --git a/layout/style/crashtests/1393580.html b/layout/style/crashtests/1393580.html new file mode 100644 index 0000000000..4a59016308 --- /dev/null +++ b/layout/style/crashtests/1393580.html @@ -0,0 +1,10 @@ + + diff --git a/layout/style/crashtests/1393791.html b/layout/style/crashtests/1393791.html new file mode 100644 index 0000000000..ad045f9a7c --- /dev/null +++ b/layout/style/crashtests/1393791.html @@ -0,0 +1,12 @@ + diff --git a/layout/style/crashtests/1395719.html b/layout/style/crashtests/1395719.html new file mode 100644 index 0000000000..5cb4aa0c7a --- /dev/null +++ b/layout/style/crashtests/1395719.html @@ -0,0 +1,19 @@ + diff --git a/layout/style/crashtests/1395725.html b/layout/style/crashtests/1395725.html new file mode 100644 index 0000000000..0ff8b4d10c --- /dev/null +++ b/layout/style/crashtests/1395725.html @@ -0,0 +1,15 @@ + + + + + + diff --git a/layout/style/crashtests/1396041.html b/layout/style/crashtests/1396041.html new file mode 100644 index 0000000000..4bca993486 --- /dev/null +++ b/layout/style/crashtests/1396041.html @@ -0,0 +1,9 @@ + diff --git a/layout/style/crashtests/1397091.html b/layout/style/crashtests/1397091.html new file mode 100644 index 0000000000..54dfb4fd51 --- /dev/null +++ b/layout/style/crashtests/1397091.html @@ -0,0 +1,13 @@ + +
    + Some anon flex item. +
    + Foo bar. + Baz +
    +
    + diff --git a/layout/style/crashtests/1397363-1.html b/layout/style/crashtests/1397363-1.html new file mode 100644 index 0000000000..5ccbfa3176 --- /dev/null +++ b/layout/style/crashtests/1397363-1.html @@ -0,0 +1,13 @@ + + + + + + + diff --git a/layout/style/crashtests/1397439-1.html b/layout/style/crashtests/1397439-1.html new file mode 100644 index 0000000000..b617f8e0ed --- /dev/null +++ b/layout/style/crashtests/1397439-1.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/style/crashtests/1398479.html b/layout/style/crashtests/1398479.html new file mode 100644 index 0000000000..85d49274a2 --- /dev/null +++ b/layout/style/crashtests/1398479.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/1398581.html b/layout/style/crashtests/1398581.html new file mode 100644 index 0000000000..e056ab44d5 --- /dev/null +++ b/layout/style/crashtests/1398581.html @@ -0,0 +1,17 @@ + + diff --git a/layout/style/crashtests/1399006.html b/layout/style/crashtests/1399006.html new file mode 100644 index 0000000000..20e9e79869 --- /dev/null +++ b/layout/style/crashtests/1399006.html @@ -0,0 +1,29 @@ + + + + + + +
    + +
    + + +
    + + + + + + +
    + +
    
    +
    + \ No newline at end of file diff --git a/layout/style/crashtests/1399546.html b/layout/style/crashtests/1399546.html new file mode 100644 index 0000000000..883ee9fd65 --- /dev/null +++ b/layout/style/crashtests/1399546.html @@ -0,0 +1,17 @@ + + diff --git a/layout/style/crashtests/1400035.html b/layout/style/crashtests/1400035.html new file mode 100644 index 0000000000..fb7a56ffbb --- /dev/null +++ b/layout/style/crashtests/1400035.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/layout/style/crashtests/1400325.html b/layout/style/crashtests/1400325.html new file mode 100644 index 0000000000..39e33a53eb --- /dev/null +++ b/layout/style/crashtests/1400325.html @@ -0,0 +1,6 @@ + diff --git a/layout/style/crashtests/1400926.html b/layout/style/crashtests/1400926.html new file mode 100644 index 0000000000..f49030759e --- /dev/null +++ b/layout/style/crashtests/1400926.html @@ -0,0 +1,10 @@ + + + diff --git a/layout/style/crashtests/1400936-1.html b/layout/style/crashtests/1400936-1.html new file mode 100644 index 0000000000..3af80a73d3 --- /dev/null +++ b/layout/style/crashtests/1400936-1.html @@ -0,0 +1,26 @@ + + +
    +
    +
    + + +
    +
    +
    + diff --git a/layout/style/crashtests/1400936-2.html b/layout/style/crashtests/1400936-2.html new file mode 100644 index 0000000000..b8cdc873da --- /dev/null +++ b/layout/style/crashtests/1400936-2.html @@ -0,0 +1,12 @@ + + + +ޜ + + diff --git a/layout/style/crashtests/1401256.html b/layout/style/crashtests/1401256.html new file mode 100644 index 0000000000..da0a579504 --- /dev/null +++ b/layout/style/crashtests/1401256.html @@ -0,0 +1,5 @@ + diff --git a/layout/style/crashtests/1401706.html b/layout/style/crashtests/1401706.html new file mode 100644 index 0000000000..7443acd8c9 --- /dev/null +++ b/layout/style/crashtests/1401706.html @@ -0,0 +1,10 @@ + + diff --git a/layout/style/crashtests/1401801.html b/layout/style/crashtests/1401801.html new file mode 100644 index 0000000000..1dedd1fff1 --- /dev/null +++ b/layout/style/crashtests/1401801.html @@ -0,0 +1,24 @@ + + + +
    + + diff --git a/layout/style/crashtests/1401825.html b/layout/style/crashtests/1401825.html new file mode 100644 index 0000000000..d42aaeefa3 --- /dev/null +++ b/layout/style/crashtests/1401825.html @@ -0,0 +1,7 @@ + + +
  • diff --git a/layout/style/crashtests/1402218-1.html b/layout/style/crashtests/1402218-1.html new file mode 100644 index 0000000000..597c1c4d5e --- /dev/null +++ b/layout/style/crashtests/1402218-1.html @@ -0,0 +1,15 @@ + + + +
    +
    +
    + + diff --git a/layout/style/crashtests/1402366.html b/layout/style/crashtests/1402366.html new file mode 100644 index 0000000000..9cffd08f04 --- /dev/null +++ b/layout/style/crashtests/1402366.html @@ -0,0 +1,10 @@ + diff --git a/layout/style/crashtests/1402419.html b/layout/style/crashtests/1402419.html new file mode 100644 index 0000000000..052de43834 --- /dev/null +++ b/layout/style/crashtests/1402419.html @@ -0,0 +1,3 @@ + diff --git a/layout/style/crashtests/1402472.html b/layout/style/crashtests/1402472.html new file mode 100644 index 0000000000..e2842a928b --- /dev/null +++ b/layout/style/crashtests/1402472.html @@ -0,0 +1,13 @@ + + diff --git a/layout/style/crashtests/1403028.html b/layout/style/crashtests/1403028.html new file mode 100644 index 0000000000..485a08efde --- /dev/null +++ b/layout/style/crashtests/1403028.html @@ -0,0 +1,9 @@ + +
    +
    diff --git a/layout/style/crashtests/1403465.html b/layout/style/crashtests/1403465.html new file mode 100644 index 0000000000..924392757b --- /dev/null +++ b/layout/style/crashtests/1403465.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/layout/style/crashtests/1403592.html b/layout/style/crashtests/1403592.html new file mode 100644 index 0000000000..184096c11c --- /dev/null +++ b/layout/style/crashtests/1403592.html @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/layout/style/crashtests/1403615.html b/layout/style/crashtests/1403615.html new file mode 100644 index 0000000000..ab2c27f7b8 --- /dev/null +++ b/layout/style/crashtests/1403615.html @@ -0,0 +1,24 @@ + + + diff --git a/layout/style/crashtests/1403712.html b/layout/style/crashtests/1403712.html new file mode 100644 index 0000000000..1166a11964 --- /dev/null +++ b/layout/style/crashtests/1403712.html @@ -0,0 +1,26 @@ + + +
    +
    + + + + +
    +
    + diff --git a/layout/style/crashtests/1404057.html b/layout/style/crashtests/1404057.html new file mode 100644 index 0000000000..4c4a33ce49 --- /dev/null +++ b/layout/style/crashtests/1404057.html @@ -0,0 +1,6 @@ + + Testcase, bug 143862 + +aaa + + \ No newline at end of file diff --git a/layout/style/crashtests/1404180-1.html b/layout/style/crashtests/1404180-1.html new file mode 100644 index 0000000000..079d6800f5 --- /dev/null +++ b/layout/style/crashtests/1404180-1.html @@ -0,0 +1,22 @@ + + +
    + + + + + + + + + +
    + + + + + + + + diff --git a/layout/style/crashtests/431705-1.xhtml b/layout/style/crashtests/431705-1.xhtml new file mode 100644 index 0000000000..8b64d4b3b2 --- /dev/null +++ b/layout/style/crashtests/431705-1.xhtml @@ -0,0 +1,6 @@ + + + + +
    + \ No newline at end of file diff --git a/layout/style/crashtests/432561-1.html b/layout/style/crashtests/432561-1.html new file mode 100644 index 0000000000..81bb082e4f --- /dev/null +++ b/layout/style/crashtests/432561-1.html @@ -0,0 +1,17 @@ + + +Testcase, Bug 432561 + + + + + diff --git a/layout/style/crashtests/437170-1.html b/layout/style/crashtests/437170-1.html new file mode 100644 index 0000000000..af6fa1d662 --- /dev/null +++ b/layout/style/crashtests/437170-1.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/layout/style/crashtests/437532-1.html b/layout/style/crashtests/437532-1.html new file mode 100644 index 0000000000..52eefa530f --- /dev/null +++ b/layout/style/crashtests/437532-1.html @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/layout/style/crashtests/439184-1.html b/layout/style/crashtests/439184-1.html new file mode 100644 index 0000000000..f22660726c --- /dev/null +++ b/layout/style/crashtests/439184-1.html @@ -0,0 +1,31 @@ + + + + Testcase, bug 439184 + + + + + + + + +

    This should be green.

    + + + diff --git a/layout/style/crashtests/444237-1.html b/layout/style/crashtests/444237-1.html new file mode 100644 index 0000000000..7eac32ed8e --- /dev/null +++ b/layout/style/crashtests/444237-1.html @@ -0,0 +1 @@ + diff --git a/layout/style/crashtests/444848-1.html b/layout/style/crashtests/444848-1.html new file mode 100644 index 0000000000..d2c75d5766 --- /dev/null +++ b/layout/style/crashtests/444848-1.html @@ -0,0 +1,9 @@ + + + diff --git a/layout/style/crashtests/447776-1.html b/layout/style/crashtests/447776-1.html new file mode 100644 index 0000000000..dde700fae1 --- /dev/null +++ b/layout/style/crashtests/447776-1.html @@ -0,0 +1,7 @@ + + + +Hang with zero width and word-wrap + +
    abc
    + diff --git a/layout/style/crashtests/447783-1.html b/layout/style/crashtests/447783-1.html new file mode 100644 index 0000000000..1af063f16b --- /dev/null +++ b/layout/style/crashtests/447783-1.html @@ -0,0 +1,8 @@ + + +Hang with column-count and word-wrap + +
    +aabcde +
    + diff --git a/layout/style/crashtests/448161-1.html b/layout/style/crashtests/448161-1.html new file mode 100644 index 0000000000..ef200462b5 --- /dev/null +++ b/layout/style/crashtests/448161-1.html @@ -0,0 +1,22 @@ + + + + + +5 + + diff --git a/layout/style/crashtests/448161-2.html b/layout/style/crashtests/448161-2.html new file mode 100644 index 0000000000..0dd8a8b530 --- /dev/null +++ b/layout/style/crashtests/448161-2.html @@ -0,0 +1,9 @@ + + + + + diff --git a/layout/style/crashtests/452150-1.xhtml b/layout/style/crashtests/452150-1.xhtml new file mode 100644 index 0000000000..4ff411c6de --- /dev/null +++ b/layout/style/crashtests/452150-1.xhtml @@ -0,0 +1,6 @@ + + + +

    + + diff --git a/layout/style/crashtests/456196.html b/layout/style/crashtests/456196.html new file mode 100644 index 0000000000..dd4b013b93 --- /dev/null +++ b/layout/style/crashtests/456196.html @@ -0,0 +1,16 @@ + + +Crash [@ nsCSSValueList::~nsCSSValueList] with adding a lot of values in css property + + +
    + + + diff --git a/layout/style/crashtests/460209-1.html b/layout/style/crashtests/460209-1.html new file mode 100644 index 0000000000..d78235738a --- /dev/null +++ b/layout/style/crashtests/460209-1.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/layout/style/crashtests/460217-1.html b/layout/style/crashtests/460217-1.html new file mode 100644 index 0000000000..e5918ad6ed --- /dev/null +++ b/layout/style/crashtests/460217-1.html @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/layout/style/crashtests/460323-1.html b/layout/style/crashtests/460323-1.html new file mode 100644 index 0000000000..0bae55aaf1 --- /dev/null +++ b/layout/style/crashtests/460323-1.html @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/layout/style/crashtests/466845-1.html b/layout/style/crashtests/466845-1.html new file mode 100644 index 0000000000..49b6b02b76 --- /dev/null +++ b/layout/style/crashtests/466845-1.html @@ -0,0 +1,14 @@ + + +Crash [@ nsViewManager::CreateView] with ::first-line position: absolute and transform + + + + +ع ع ع + + + + diff --git a/layout/style/crashtests/469432-1.xhtml b/layout/style/crashtests/469432-1.xhtml new file mode 100644 index 0000000000..9b11a88c42 --- /dev/null +++ b/layout/style/crashtests/469432-1.xhtml @@ -0,0 +1,8 @@ + + + +
    + diff --git a/layout/style/crashtests/1404316.html b/layout/style/crashtests/1404316.html new file mode 100644 index 0000000000..4ac2f16701 --- /dev/null +++ b/layout/style/crashtests/1404316.html @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/layout/style/crashtests/1404324-1.html b/layout/style/crashtests/1404324-1.html new file mode 100644 index 0000000000..574a5437cb --- /dev/null +++ b/layout/style/crashtests/1404324-1.html @@ -0,0 +1,12 @@ + + diff --git a/layout/style/crashtests/1404324-2.html b/layout/style/crashtests/1404324-2.html new file mode 100644 index 0000000000..797347d5c0 --- /dev/null +++ b/layout/style/crashtests/1404324-2.html @@ -0,0 +1,10 @@ + + + diff --git a/layout/style/crashtests/1404324-3.html b/layout/style/crashtests/1404324-3.html new file mode 100644 index 0000000000..3b06f12a2b --- /dev/null +++ b/layout/style/crashtests/1404324-3.html @@ -0,0 +1,14 @@ + + + diff --git a/layout/style/crashtests/1405880.html b/layout/style/crashtests/1405880.html new file mode 100644 index 0000000000..aad53507a7 --- /dev/null +++ b/layout/style/crashtests/1405880.html @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/layout/style/crashtests/1406222-1.html b/layout/style/crashtests/1406222-1.html new file mode 100644 index 0000000000..218a3da095 --- /dev/null +++ b/layout/style/crashtests/1406222-1.html @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/layout/style/crashtests/1406222-2.html b/layout/style/crashtests/1406222-2.html new file mode 100644 index 0000000000..3ae655d972 --- /dev/null +++ b/layout/style/crashtests/1406222-2.html @@ -0,0 +1,15 @@ + + + +Some text diff --git a/layout/style/crashtests/1409183.html b/layout/style/crashtests/1409183.html new file mode 100644 index 0000000000..7fe21032cd --- /dev/null +++ b/layout/style/crashtests/1409183.html @@ -0,0 +1,15 @@ + + + + +Title +
    + + +
    + + + + + + + +
    + + + diff --git a/layout/style/crashtests/495269-1.html b/layout/style/crashtests/495269-1.html new file mode 100644 index 0000000000..40090edc8c --- /dev/null +++ b/layout/style/crashtests/495269-1.html @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/layout/style/crashtests/495269-2.html b/layout/style/crashtests/495269-2.html new file mode 100644 index 0000000000..8deca08e81 --- /dev/null +++ b/layout/style/crashtests/495269-2.html @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/layout/style/crashtests/498036-1.html b/layout/style/crashtests/498036-1.html new file mode 100644 index 0000000000..0128be7491 --- /dev/null +++ b/layout/style/crashtests/498036-1.html @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/layout/style/crashtests/509155-1.html b/layout/style/crashtests/509155-1.html new file mode 100644 index 0000000000..d211373326 --- /dev/null +++ b/layout/style/crashtests/509155-1.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/crashtests/509156-1.html b/layout/style/crashtests/509156-1.html new file mode 100644 index 0000000000..f75776a5d3 --- /dev/null +++ b/layout/style/crashtests/509156-1.html @@ -0,0 +1,5 @@ + + + +
    + diff --git a/layout/style/crashtests/509569-1.html b/layout/style/crashtests/509569-1.html new file mode 100644 index 0000000000..41a3cc5594 --- /dev/null +++ b/layout/style/crashtests/509569-1.html @@ -0,0 +1,2 @@ + + diff --git a/layout/style/crashtests/512851-1.xhtml b/layout/style/crashtests/512851-1.xhtml new file mode 100644 index 0000000000..d772390cd1 --- /dev/null +++ b/layout/style/crashtests/512851-1.xhtml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/layout/style/crashtests/524252-1.html b/layout/style/crashtests/524252-1.html new file mode 100644 index 0000000000..4ea708bc39 --- /dev/null +++ b/layout/style/crashtests/524252-1.html @@ -0,0 +1,10 @@ + + +a + diff --git a/layout/style/crashtests/536789-1.html b/layout/style/crashtests/536789-1.html new file mode 100644 index 0000000000..86fcb344f7 --- /dev/null +++ b/layout/style/crashtests/536789-1.html @@ -0,0 +1,11 @@ + + + + + diff --git a/layout/style/crashtests/539613-1.xhtml b/layout/style/crashtests/539613-1.xhtml new file mode 100644 index 0000000000..386d7e1277 --- /dev/null +++ b/layout/style/crashtests/539613-1.xhtml @@ -0,0 +1,5 @@ + + +f + + diff --git a/layout/style/crashtests/558943-1.xhtml b/layout/style/crashtests/558943-1.xhtml new file mode 100644 index 0000000000..e3d978fd34 --- /dev/null +++ b/layout/style/crashtests/558943-1.xhtml @@ -0,0 +1,11 @@ + + + + + +
    Link
    + + + +
    + + +
    + diff --git a/layout/style/crashtests/930270-1.html b/layout/style/crashtests/930270-1.html new file mode 100644 index 0000000000..cbdf5a9e38 --- /dev/null +++ b/layout/style/crashtests/930270-1.html @@ -0,0 +1,6 @@ + + + + + diff --git a/layout/style/crashtests/972199-1.html b/layout/style/crashtests/972199-1.html new file mode 100644 index 0000000000..a4e0de0d81 --- /dev/null +++ b/layout/style/crashtests/972199-1.html @@ -0,0 +1,37 @@ + + + + + + +
    + + diff --git a/layout/style/crashtests/989965-1.html b/layout/style/crashtests/989965-1.html new file mode 100644 index 0000000000..a2879d973f --- /dev/null +++ b/layout/style/crashtests/989965-1.html @@ -0,0 +1,9 @@ + + + + diff --git a/layout/style/crashtests/992333-1.html b/layout/style/crashtests/992333-1.html new file mode 100644 index 0000000000..86a1d57d73 --- /dev/null +++ b/layout/style/crashtests/992333-1.html @@ -0,0 +1,10 @@ + + +

    Hello.

    + diff --git a/layout/style/crashtests/blue-32x32.png b/layout/style/crashtests/blue-32x32.png new file mode 100644 index 0000000000..deefd19b2a Binary files /dev/null and b/layout/style/crashtests/blue-32x32.png differ diff --git a/layout/style/crashtests/border-image-visited-link.html b/layout/style/crashtests/border-image-visited-link.html new file mode 100644 index 0000000000..b6e3ae5d78 --- /dev/null +++ b/layout/style/crashtests/border-image-visited-link.html @@ -0,0 +1,10 @@ + +border-image on link with visited styles + +test diff --git a/layout/style/crashtests/content-only-on-link-before.html b/layout/style/crashtests/content-only-on-link-before.html new file mode 100644 index 0000000000..949e13542f --- /dev/null +++ b/layout/style/crashtests/content-only-on-link-before.html @@ -0,0 +1,5 @@ + + +example diff --git a/layout/style/crashtests/content-only-on-visited-before.html b/layout/style/crashtests/content-only-on-visited-before.html new file mode 100644 index 0000000000..4496bb86df --- /dev/null +++ b/layout/style/crashtests/content-only-on-visited-before.html @@ -0,0 +1,5 @@ + + +example diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list new file mode 100644 index 0000000000..3e7d1e2669 --- /dev/null +++ b/layout/style/crashtests/crashtests.list @@ -0,0 +1,325 @@ +load 105619-1.html +load 187671-1.html +load 192408-1.html +load 285727-1.html +load 286707-1.html +load 317561-1.html +load 330998-1.html +load 363950.html +load 368175-1.html +load 368740.html +load 379788-1.html +load 383979-1.xhtml +load 383979-2.html +load 386939-1.html +load 391034-1.xhtml +load 397022-1.html +load 399289-1.svg +load 404470-1.html +load 411603-1.html +load 412588-1.html +load 413274-1.xhtml +skip-if(Android) load chrome://reftest/content/crashtests/layout/style/crashtests/416461-1.xhtml +load 418007-1.xhtml +load chrome://reftest/content/crashtests/layout/style/crashtests/431705-1.xhtml +load 432561-1.html +load 437170-1.html +load 437532-1.html +skip-if(ThreadSanitizer) load 439184-1.html +load 444237-1.html +load 444848-1.html +load 447776-1.html +load 447783-1.html +load 448161-1.html +load 448161-2.html +load 452150-1.xhtml +load 456196.html +load 460209-1.html +load 460217-1.html +load 460323-1.html +load 466845-1.html +load 469432-1.xhtml +load 472195-1.html +load 472237-1.html # will fail, test for leak (474704) +HTTP(..) load 472237-1.html +load 473720-1.html +load 473892-1.html +load 473914-1.html +load 474377-1.xhtml +load 478321-1.xhtml +load 481557.html +load 495269-1.html +load 495269-2.html +load 498036-1.html +load 509155-1.html +load 509156-1.html +load 509569-1.html +load 512851-1.xhtml +load 524252-1.html +load 536789-1.html +load 539613-1.xhtml +load 558943-1.xhtml +load 559491.html +load 565248-1.html +load 571105-1.xhtml +load 573127-1.html +load 575464-1.html +load 580685.html +load 585185-1.html +load 588627-1.html +load 592698-1.html +load 601437-1.html +load 601439-1.html +load 605689-1.html +load 611922-1.html +load 612213.html +load 621596-1.html +load 622314-1.xhtml +load 635153.html +load 637242.xhtml +load 645142.html +load 652976-1.svg +load 653675.html +load 665209-1.html +load 671799-1.html +load 671799-2.html +load 690990-1.html +load 694775.html +load 696188-1.html +load 696869-1.html +load 700116.html +load 729126-1.html +load 729126-2.html +load 786108-1.html +load 786108-2.html +load 788836.html +load 806310-1.html +load 809762.html +load 812824.html +load 822766-1.html +load 822877.html +load 827213-1.html +load 827220.html +load 827591-1.html +load 829817.html +load 842134.html +load 861489-1.html +load 862113.html +load 867487.html +load 873222.html +load 873260-1.html +load 873260-2.html +load 880862.html +load 894245-1.html +load 915440.html +load 927734-1.html +load 930270-1.html +load 930270-2.html +load 945048-1.html +load 972199-1.html +load 989965-1.html +load 992333-1.html +load 1017798-1.html +load 1028514-1.html +load 1066089-1.html +load 1074651-1.html +load 1135534.html +load 1089463-1.html +load 1136010-1.html +load 1146101-1.html +load 1153693-1.html +load 1156969.svg +load 1161320-1.html +load 1161320-2.html +load 1161366-1.html +load 1163446-1.html +load 1164813-1.html +load 1167782-1.html +load 1186768-1.xhtml +load 1200568-1.html +load 1206105-1.html +load 1223688-1.html +load 1223694-1.html +load 1226400-1.html +load 1227498.html +load 1227501-1.html +load 1228789-1.html +load 1230408-1.html +load 1233135-1.html +load 1233135-2.html +load 1236398.xhtml +load 1238660-1.html +load 1245260-1.html +load 1247865-1.html +load 1250791.html +load 1264396-1.html +load 1264949.html +load 1265611-1.html +load 1270795.html +load 1275026.html +load 1278463-1.html +load 1277908-1.html +load 1277908-2.html +load 1282076-1.html +load 1282076-2.html +load 1290994-1.html +load 1290994-2.html +load 1290994-3.html +load 1290994-4.html +load 1314531.html +load 1315889-1.html +load 1315894-1.html +pref(widget.windows.window_occlusion_tracking.enabled,false) skip-if(wayland) load 1319072-1.html # Bug 1819154, wayland: bug 1856389 +HTTP load 1320423-1.html +load 1321357-1.html +load 1328535-1.html +load 1331272.html +load 1332550.html +HTTP load 1333001-1.html +load 1340248.html +skip-if(wayland) load 1340344.html # wayland: bug 1856389 +skip-if(wayland) load 1342316-1.html # wayland: bug 1856389 +load 1344210.html +load 1353312.html +load 1356601-1.html +load 1364139-1.html +load 1371450-1.html +load 1374175-1.html +load 1375812-1.html +load 1377053-1.html +load 1377256-1.html +skip-if(wayland) load 1378064-1.html # wayland: bug 1856389 +load 1378814.html +load 1380800.html +load link-transition-before.html +skip-if(wayland) load 1381420-1.html # wayland: bug 1856389 +load 1381682.html +load 1382672.html +load 1382710.html +pref(dom.animations-api.compositing.enabled,true) load 1383493-1.html +skip-if(wayland) load 1383001.html # wayland: bug 1856389 +load 1383001-2.html +load 1383319.html +skip-if(wayland) load 1383589-1.html # wayland: bug 1856389 +load 1383975.html +load border-image-visited-link.html +load content-only-on-link-before.html +load content-only-on-visited-before.html +load font-face-truncated-src.html +load large_border_image_width.html +load link-transition-before.html +load long-url-list-stack-overflow.html #Bug 1525117 +skip-if(wayland) load scale-on-block-continuation.html # wayland: bug 1856389 +skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981.html # Very sensitive to stack size. +skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981-2.html +skip-if(AddressSanitizer) skip-if(ThreadSanitizer) load 1383981-3.html +load 1384824-1.html +load 1384824-2.html +load 1386773.html +load 1387481-1.html +load 1387499.html +load 1388234.html +load 1391577.html +load 1393189.html +load 1393580.html +load 1389645.html +load 1390726.html +load 1393791.html +load 1384232.html +load 1395725.html +load 1396041.html +pref(dom.animations-api.compositing.enabled,true) load 1397363-1.html +load 1397439-1.html +load 1395719.html +load 1397091.html +load 1398479.html +load 1398581.html +load 1399006.html +load 1399546.html +load 1400035.html +load 1400325.html +load 1400926.html +load 1400936-1.html +load 1400936-2.html +load 1401256.html +load 1401706.html +skip-if(wayland) load 1401801.html # wayland: bug 1856389 +load 1401825.html +load 1402218-1.html +load 1402366.html +load 1402419.html +load 1402472.html +load 1403028.html +load 1403433.html +load 1403465.html +load 1403592.html +load 1403615.html +load 1403712.html +load 1404180-1.html +load 1404316.html +load 1406222-1.html +load 1406222-2.html +load 1404324-1.html +load 1404324-2.html +load 1404324-3.html +load 1404057.html +load 1405880.html +load 1409502.html +load 1409931.html +load 1410226-1.html +load 1410226-2.html +load 1411008.html +load 1411143.html +load 1411478.html +load 1413288.html +load 1413361.html +load 1413670.html +load 1415353.html +skip-if(wayland) load 1418059.html # wayland: bug 1856389 +skip-if(wayland) load 1418867.html # wayland: bug 1856389 +load 1419554.html +load 1426312.html +load 1439793.html +load 1409183.html +load 1445682.html +load 1449243.html +load 1450691.html +load 1453206.html +load 1454140.html +load 1455108.html +load 1457288.html +load 1457985.html +load 1468640.html +load 1469076.html +load 1475003.html +load 1479681.html +load 1488817.html +load 1490012.html +load 1502893.html +load 1507674.html +load 1509989.html +load 1514086.html +load 1533783.html +load 1533891.html +pref(gfx.omta.background-color,true) load 1533968.html +load 1545177.html +skip-if(geckoview) skip-if(Android) load 1546255.html # Bug 1563020 for GV+WR & Bug 1553971 +load 1552911.html +load 1562361.html +load 1566684.html +load 1579788.html +load 1580307.html +load 1581579.html +skip-if(release_or_beta) pref(dom.paintWorklet.enabled,true) load 1593766.html # bug 1581896 +load 1594949.html +pref(layout.css.individual-transform.enabled,true) load 1594960.html +load 1586444.html +load 1599286.html +load 1609786.html +load 1616433.html +load 1616407.html +load 1639533.html +pref(layout.accessiblecaret.enabled,true) load 1640040.html +load 1806189-1.html +pref(layout.css.scroll-driven-animations.enabled,true) load 1821416.html +pref(layout.css.individual-transform.enabled,true) pref(dom.animations-api.compositing.enabled,true) load 1872309.html diff --git a/layout/style/crashtests/font-face-truncated-src.html b/layout/style/crashtests/font-face-truncated-src.html new file mode 100644 index 0000000000..c3d7dbda5b --- /dev/null +++ b/layout/style/crashtests/font-face-truncated-src.html @@ -0,0 +1,2 @@ + + diff --git a/layout/style/crashtests/large_border_image_width.html b/layout/style/crashtests/large_border_image_width.html new file mode 100644 index 0000000000..d80b734654 --- /dev/null +++ b/layout/style/crashtests/large_border_image_width.html @@ -0,0 +1,9 @@ + + + + + +
    + + + diff --git a/layout/style/crashtests/link-transition-before.html b/layout/style/crashtests/link-transition-before.html new file mode 100644 index 0000000000..6a8b7e409e --- /dev/null +++ b/layout/style/crashtests/link-transition-before.html @@ -0,0 +1,27 @@ + + + +example + \ No newline at end of file diff --git a/layout/style/crashtests/long-url-list-stack-overflow.html b/layout/style/crashtests/long-url-list-stack-overflow.html new file mode 100644 index 0000000000..899e858df5 --- /dev/null +++ b/layout/style/crashtests/long-url-list-stack-overflow.html @@ -0,0 +1,23 @@ + + + + + + + +
    + + diff --git a/layout/style/crashtests/scale-on-block-continuation.html b/layout/style/crashtests/scale-on-block-continuation.html new file mode 100644 index 0000000000..e8f27e607d --- /dev/null +++ b/layout/style/crashtests/scale-on-block-continuation.html @@ -0,0 +1,43 @@ + + + +Test for applying a scale animation to a block continuation + + +
    +

    Neque et soluta consectetur. Quia quo magnam ipsa modi. Aspernatur necessitatibus consequatur facere voluptates rerum omnis iusto earum. Beatae quisquam odio est. Deleniti distinctio doloribus veniam similique voluptas est aut. Dignissimos dignissimos voluptas illo odit.

    +

    Laborum quae reprehenderit alias. Incidunt aliquam non sint non eaque et itaque aut. Est quaerat neque explicabo id voluptas reiciendis. Et animi odio eligendi ipsa repudiandae quam iure. Commodi asperiores sapiente dolorem assumenda debitis. Soluta quisquam porro fugiat fugiat sapiente et excepturi rem.

    +

    Ipsam eligendi neque perspiciatis est aut ea nihil. Eum sit ipsa sunt aut voluptatem optio. Qui quae autem aspernatur. Et perspiciatis alias voluptatem.

    +
    + + diff --git a/layout/style/designmode.css b/layout/style/designmode.css new file mode 100644 index 0000000000..194586ee1d --- /dev/null +++ b/layout/style/designmode.css @@ -0,0 +1,8 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +*|* { + -moz-user-modify: read-write; +} diff --git a/layout/style/extra-bindgen-flags.in b/layout/style/extra-bindgen-flags.in new file mode 100644 index 0000000000..862f2f7c45 --- /dev/null +++ b/layout/style/extra-bindgen-flags.in @@ -0,0 +1 @@ +@BINDGEN_SYSTEM_FLAGS@ @NSPR_CFLAGS@ @MOZ_PIXMAN_CFLAGS@ @MOZ_ICU_CFLAGS@ diff --git a/layout/style/jar.mn b/layout/style/jar.mn new file mode 100644 index 0000000000..4467a9d79e --- /dev/null +++ b/layout/style/jar.mn @@ -0,0 +1,35 @@ +# 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/. + +toolkit.jar: + res/ua.css (res/ua.css) + res/html.css (res/html.css) + res/quirk.css (res/quirk.css) + res/counterstyles.css (res/counterstyles.css) + res/noframes.css (res/noframes.css) + res/scrollbars.css (res/scrollbars.css) + res/forms.css (res/forms.css) + #ifdef ANDROID + res/accessiblecaret-normal.svg (res/accessiblecaret-normal.svg) + res/accessiblecaret-tilt-left.svg (res/accessiblecaret-tilt-left.svg) + res/accessiblecaret-tilt-right.svg (res/accessiblecaret-tilt-right.svg) + #else + res/accessiblecaret-normal@1x.png (res/accessiblecaret-normal@1x.png) + res/accessiblecaret-normal@1.5x.png (res/accessiblecaret-normal@1.5x.png) + res/accessiblecaret-normal@2x.png (res/accessiblecaret-normal@2x.png) + res/accessiblecaret-normal@2.25x.png (res/accessiblecaret-normal@2.25x.png) + res/accessiblecaret-tilt-left@1x.png (res/accessiblecaret-tilt-left@1x.png) + res/accessiblecaret-tilt-left@1.5x.png (res/accessiblecaret-tilt-left@1.5x.png) + res/accessiblecaret-tilt-left@2x.png (res/accessiblecaret-tilt-left@2x.png) + res/accessiblecaret-tilt-left@2.25x.png (res/accessiblecaret-tilt-left@2.25x.png) + res/accessiblecaret-tilt-right@1x.png (res/accessiblecaret-tilt-right@1x.png) + res/accessiblecaret-tilt-right@1.5x.png (res/accessiblecaret-tilt-right@1.5x.png) + res/accessiblecaret-tilt-right@2x.png (res/accessiblecaret-tilt-right@2x.png) + res/accessiblecaret-tilt-right@2.25x.png (res/accessiblecaret-tilt-right@2.25x.png) + #endif + res/password.svg (res/password.svg) + res/password-hide.svg (res/password-hide.svg) + +% resource gre-resources %res/ +% resource content-accessible resource://gre/contentaccessible/ contentaccessible=yes diff --git a/layout/style/moz.build b/layout/style/moz.build new file mode 100644 index 0000000000..a14ab6a7ac --- /dev/null +++ b/layout/style/moz.build @@ -0,0 +1,353 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "CSS Parsing and Computation") + +with Files("nsComputedDOMStyle.*"): + BUG_COMPONENT = ("Core", "DOM: CSS Object Model") + +with Files("nsROCSSPrimitiveValue.*"): + BUG_COMPONENT = ("Core", "DOM: CSS Object Model") + +with Files("CSSRuleList.*"): + BUG_COMPONENT = ("Core", "DOM: CSS Object Model") + +with Files("nsDOM*"): + BUG_COMPONENT = ("Core", "DOM: CSS Object Model") + +with Files("AnimationCollection.*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +with Files("AnimatedPropertyID*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +with Files("AnimationCommon.*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +with Files("nsAnimationManager.*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +with Files("nsTransitionManager.*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +with Files("StyleAnimationValue.*"): + BUG_COMPONENT = ("Core", "CSS Transitions and Animations") + +TEST_DIRS += ["test"] + +EXPORTS += [ + "!nsCSSPropertyID.h", + "AnimationCommon.h", + "CounterStyleManager.h", + "nsAnimationManager.h", + "nsComputedDOMStyle.h", + "nsCSSAnonBoxes.h", + "nsCSSAnonBoxList.h", + "nsCSSCounterDescList.h", + "nsCSSFontDescList.h", + "nsCSSPropertyIDSet.h", + "nsCSSProps.h", + "nsCSSPseudoElementList.h", + "nsCSSPseudoElements.h", + "nsCSSValue.h", + "nsDOMCSSAttrDeclaration.h", + "nsDOMCSSDeclaration.h", + "nsICSSDeclaration.h", + "nsICSSLoaderObserver.h", + "nsStyleAutoArray.h", + "nsStyleConsts.h", + "nsStyleStruct.h", + "nsStyleStructFwd.h", + "nsStyleStructInlines.h", + "nsStyleStructList.h", + "nsStyleTransformMatrix.h", + "nsStyleUtil.h", +] + +EXPORTS.mozilla += [ + "!ServoCSSPropList.h", + "AnimatedPropertyID.h", + "AnimatedPropertyIDSet.h", + "AnimationCollection.h", + "AttributeStyles.h", + "CachedInheritingStyles.h", + "ComputedStyle.h", + "ComputedStyleInlines.h", + "CSSEnabledState.h", + "CSSPropFlags.h", + "DeclarationBlock.h", + "DocumentStyleRootIterator.h", + "FontLoaderUtils.h", + "FontPreloader.h", + "GeckoBindings.h", + "GlobalStyleSheetCache.h", + "ImportScanner.h", + "LayerAnimationInfo.h", + "MappedDeclarationsBuilder.h", + "MediaFeatureChange.h", + "PostTraversalTask.h", + "PreferenceSheet.h", + "PreloadedStyleSheet.h", + "PseudoStyleType.h", + "RustCell.h", + "ServoBindings.h", + "ServoBindingTypes.h", + "ServoBoxedTypeList.h", + "ServoComputedData.h", + "ServoCSSParser.h", + "ServoCSSRuleList.h", + "ServoElementSnapshot.h", + "ServoElementSnapshotTable.h", + "ServoLockedArcTypeList.h", + "ServoStyleConstsForwards.h", + "ServoStyleConstsInlines.h", + "ServoStyleSet.h", + "ServoStyleSetInlines.h", + "ServoTraversalStatistics.h", + "ServoTypes.h", + "ServoUtils.h", + "ShadowParts.h", + "SharedStyleSheetCache.h", + "SharedSubResourceCache.h", + "StyleAnimationValue.h", + "StyleColorInlines.h", + "StyleSheet.h", + "StyleSheetInfo.h", + "StyleSheetInlines.h", + "TimelineCollection.h", + "TimelineManager.h", + "URLExtraData.h", + "UserAgentStyleSheetID.h", + "UserAgentStyleSheetList.h", +] + +EXPORTS.mozilla.dom += [ + "CSS.h", + "CSSContainerRule.h", + "CSSCounterStyleRule.h", + "CSSFontFaceRule.h", + "CSSFontFeatureValuesRule.h", + "CSSFontPaletteValuesRule.h", + "CSSImportRule.h", + "CSSKeyframeRule.h", + "CSSKeyframesRule.h", + "CSSLayerBlockRule.h", + "CSSLayerStatementRule.h", + "CSSMediaRule.h", + "CSSMozDocumentRule.h", + "CSSNamespaceRule.h", + "CSSPageRule.h", + "CSSPropertyRule.h", + "CSSRuleList.h", + "CSSStyleRule.h", + "CSSSupportsRule.h", + "CSSValue.h", + "FontFace.h", + "FontFaceImpl.h", + "FontFaceSet.h", + "FontFaceSetDocumentImpl.h", + "FontFaceSetImpl.h", + "FontFaceSetIterator.h", + "FontFaceSetWorkerImpl.h", + "MediaList.h", + "MediaQueryList.h", + "PaintWorkletGlobalScope.h", +] + +EXPORTS.mozilla.css += [ + "DocumentMatchingFunction.h", + "ErrorReporter.h", + "GroupRule.h", + "ImageLoader.h", + "Loader.h", + "Rule.h", + "SheetLoadData.h", + "SheetParsingMode.h", + "StreamLoader.h", + "StylePreloadKind.h", +] + +UNIFIED_SOURCES += [ + "AnimationCollection.cpp", + "AttributeStyles.cpp", + "CachedInheritingStyles.cpp", + "ComputedStyle.cpp", + "CounterStyleManager.cpp", + "CSS.cpp", + "CSSContainerRule.cpp", + "CSSCounterStyleRule.cpp", + "CSSFontFaceRule.cpp", + "CSSFontFeatureValuesRule.cpp", + "CSSFontPaletteValuesRule.cpp", + "CSSImportRule.cpp", + "CSSKeyframeRule.cpp", + "CSSKeyframesRule.cpp", + "CSSLayerBlockRule.cpp", + "CSSLayerStatementRule.cpp", + "CSSMediaRule.cpp", + "CSSMozDocumentRule.cpp", + "CSSNamespaceRule.cpp", + "CSSPageRule.cpp", + "CSSPropertyRule.cpp", + "CSSRuleList.cpp", + "CSSStyleRule.cpp", + "CSSSupportsRule.cpp", + "DeclarationBlock.cpp", + "DocumentStyleRootIterator.cpp", + "ErrorReporter.cpp", + "FontFace.cpp", + "FontFaceImpl.cpp", + "FontFaceSet.cpp", + "FontFaceSetDocumentImpl.cpp", + "FontFaceSetImpl.cpp", + "FontFaceSetIterator.cpp", + "FontFaceSetWorkerImpl.cpp", + "FontLoaderUtils.cpp", + "FontPreloader.cpp", + "GeckoBindings.cpp", + "GlobalStyleSheetCache.cpp", + "GroupRule.cpp", + "ImageLoader.cpp", + "ImportScanner.cpp", + "LayerAnimationInfo.cpp", + "Loader.cpp", + "MappedDeclarationsBuilder.cpp", + "MediaList.cpp", + "MediaQueryList.cpp", + "nsAnimationManager.cpp", + "nsComputedDOMStyle.cpp", + "nsCSSAnonBoxes.cpp", + "nsCSSProps.cpp", + "nsCSSPseudoElements.cpp", + "nsCSSValue.cpp", + "nsDOMCSSAttrDeclaration.cpp", + "nsDOMCSSDeclaration.cpp", + "nsDOMCSSValueList.cpp", + "nsFontFaceLoader.cpp", + "nsFontFaceUtils.cpp", + "nsICSSDeclaration.cpp", + "nsMediaFeatures.cpp", + "nsROCSSPrimitiveValue.cpp", + "nsStyleStruct.cpp", + "nsStyleTransformMatrix.cpp", + "nsStyleUtil.cpp", + "nsTransitionManager.cpp", + "PaintWorkletGlobalScope.cpp", + "PaintWorkletImpl.cpp", + "PostTraversalTask.cpp", + "PreferenceSheet.cpp", + "PreloadedStyleSheet.cpp", + "PseudoStyleType.cpp", + "Rule.cpp", + "ServoCSSParser.cpp", + "ServoCSSRuleList.cpp", + "ServoElementSnapshot.cpp", + "ServoStyleSet.cpp", + "ShadowParts.cpp", + "SharedStyleSheetCache.cpp", + "StreamLoader.cpp", + "StyleAnimationValue.cpp", + "StyleColor.cpp", + "StyleSheet.cpp", + "TimelineCollection.cpp", + "TimelineManager.cpp", + "URLExtraData.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +LOCAL_INCLUDES += [ + "../base", + "../generic", + "../xul", + "/dom/base", + "/dom/html", + "/dom/xul", + "/image", +] + +JAR_MANIFESTS += ["jar.mn"] + +RESOURCE_FILES += [ + "contenteditable.css", + "designmode.css", +] + +CONTENT_ACCESSIBLE_FILES += [ + "ImageDocument.css", + "res/accessiblecaret.css", + "res/details.css", + "res/plaintext.css", + "res/searchfield-cancel.svg", + "res/viewsource.css", + "TopLevelImageDocument.css", + "TopLevelVideoDocument.css", +] + + +GeneratedFile( + "nsCSSPropertyID.h", + script="GenerateCSSPropertyID.py", + entry_point="generate", + inputs=["nsCSSPropertyID.h.in", "!ServoCSSPropList.py"], +) +GeneratedFile( + "ServoCSSPropList.h", + script="GenerateServoCSSPropList.py", + entry_point="generate_header", + inputs=["!ServoCSSPropList.py"], +) +GeneratedFile( + "ServoCSSPropList.py", + script="GenerateServoCSSPropList.py", + entry_point="generate_data", + inputs=["ServoCSSPropList.mako.py"], +) + +if CONFIG["COMPILE_ENVIRONMENT"]: + EXPORTS.mozilla += [ + "!CompositorAnimatableProperties.h", + "!CountedUnknownProperties.h", + "!ServoStyleConsts.h", + ] + + GeneratedFile( + "CompositorAnimatableProperties.h", + script="GenerateCompositorAnimatableProperties.py", + entry_point="generate", + inputs=["!ServoCSSPropList.py"], + ) + GeneratedFile( + "CountedUnknownProperties.h", + script="GenerateCountedUnknownProperties.py", + entry_point="generate", + inputs=[ + "/servo/components/style/properties/counted_unknown_properties.py", + ], + ) + GeneratedFile( + "nsComputedDOMStyleGenerated.inc", + script="GenerateComputedDOMStyleGenerated.py", + entry_point="generate", + inputs=["!ServoCSSPropList.py"], + ) + GeneratedFile( + "nsCSSPropsGenerated.inc", + script="GenerateCSSPropsGenerated.py", + entry_point="generate", + inputs=["!ServoCSSPropList.py"], + ) + CbindgenHeader( + "ServoStyleConsts.h", + inputs=["/servo/ports/geckolib", "/servo/components/style"], + ) + + CONFIGURE_SUBST_FILES += [ + "extra-bindgen-flags", + ] diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp new file mode 100644 index 0000000000..978755cae2 --- /dev/null +++ b/layout/style/nsAnimationManager.cpp @@ -0,0 +1,467 @@ +/* -*- 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 "nsAnimationManager.h" +#include "nsINode.h" +#include "nsTransitionManager.h" + +#include "mozilla/AnimationEventDispatcher.h" +#include "mozilla/AnimationUtils.h" +#include "mozilla/EffectCompositor.h" +#include "mozilla/ElementAnimationData.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ServoStyleSet.h" +#include "mozilla/TimelineCollection.h" +#include "mozilla/dom/AnimationEffect.h" +#include "mozilla/dom/DocumentTimeline.h" +#include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/dom/MutationObservers.h" +#include "mozilla/dom/ScrollTimeline.h" +#include "mozilla/dom/ViewTimeline.h" + +#include "nsPresContext.h" +#include "nsPresContextInlines.h" +#include "nsStyleChangeList.h" +#include "nsLayoutUtils.h" +#include "nsIFrame.h" +#include "mozilla/dom/Document.h" +#include "nsDOMMutationObserver.h" +#include "nsRFPService.h" +#include // std::stable_sort +#include + +using namespace mozilla; +using namespace mozilla::css; +using mozilla::dom::Animation; +using mozilla::dom::AnimationPlayState; +using mozilla::dom::CSSAnimation; +using mozilla::dom::Element; +using mozilla::dom::KeyframeEffect; +using mozilla::dom::MutationObservers; +using mozilla::dom::ScrollTimeline; +using mozilla::dom::ViewTimeline; + +////////////////////////// nsAnimationManager //////////////////////////// + +// Find the matching animation by |aName| in the old list +// of animations and remove the matched animation from the list. +static already_AddRefed PopExistingAnimation( + const nsAtom* aName, + nsAnimationManager::CSSAnimationCollection* aCollection) { + if (!aCollection) { + return nullptr; + } + + // Animations are stored in reverse order to how they appear in the + // animation-name property. However, we want to match animations beginning + // from the end of the animation-name list, so we iterate *forwards* + // through the collection. + for (size_t idx = 0, length = aCollection->mAnimations.Length(); + idx != length; ++idx) { + CSSAnimation* cssAnim = aCollection->mAnimations[idx]; + if (cssAnim->AnimationName() == aName) { + RefPtr match = cssAnim; + aCollection->mAnimations.RemoveElementAt(idx); + return match.forget(); + } + } + + return nullptr; +} + +class MOZ_STACK_CLASS ServoCSSAnimationBuilder final { + public: + explicit ServoCSSAnimationBuilder(const ComputedStyle* aComputedStyle) + : mComputedStyle(aComputedStyle) { + MOZ_ASSERT(aComputedStyle); + } + + bool BuildKeyframes(const Element& aElement, nsPresContext* aPresContext, + nsAtom* aName, + const StyleComputedTimingFunction& aTimingFunction, + nsTArray& aKeyframes) { + return aPresContext->StyleSet()->GetKeyframesForName( + aElement, *mComputedStyle, aName, aTimingFunction, aKeyframes); + } + void SetKeyframes(KeyframeEffect& aEffect, nsTArray&& aKeyframes, + const dom::AnimationTimeline* aTimeline) { + aEffect.SetKeyframes(std::move(aKeyframes), mComputedStyle, aTimeline); + } + + // Currently all the animation building code in this file is based on + // assumption that creating and removing animations should *not* trigger + // additional restyles since those changes will be handled within the same + // restyle. + // + // While that is true for the Gecko style backend, it is not true for the + // Servo style backend where we want restyles to be triggered so that we + // perform a second animation restyle where we will incorporate the changes + // arising from creating and removing animations. + // + // Fortunately, our attempts to avoid posting extra restyles as part of the + // processing here are imperfect and most of the time we happen to post + // them anyway. Occasionally, however, we don't. For example, we don't post + // a restyle when we create a new animation whose an animation index matches + // the default value it was given already (which is typically only true when + // the CSSAnimation we create is the first Animation created in a particular + // content process). + // + // As a result, when we are using the Servo backend, whenever we have an added + // or removed animation we need to explicitly trigger a restyle. + // + // This code should eventually disappear along with the Gecko style backend + // and we should simply call Play() / Pause() / Cancel() etc. which will + // post the required restyles. + void NotifyNewOrRemovedAnimation(const Animation& aAnimation) { + dom::AnimationEffect* effect = aAnimation.GetEffect(); + if (!effect) { + return; + } + + KeyframeEffect* keyframeEffect = effect->AsKeyframeEffect(); + if (!keyframeEffect) { + return; + } + + keyframeEffect->RequestRestyle(EffectCompositor::RestyleType::Standard); + } + + private: + const ComputedStyle* mComputedStyle; +}; + +static void UpdateOldAnimationPropertiesWithNew( + CSSAnimation& aOld, TimingParams&& aNewTiming, + nsTArray&& aNewKeyframes, bool aNewIsStylePaused, + CSSAnimationProperties aOverriddenProperties, + ServoCSSAnimationBuilder& aBuilder, dom::AnimationTimeline* aTimeline, + dom::CompositeOperation aNewComposite) { + bool animationChanged = false; + + // Update the old from the new so we can keep the original object + // identity (and any expando properties attached to it). + if (aOld.GetEffect()) { + dom::AnimationEffect* oldEffect = aOld.GetEffect(); + + // Copy across the changes that are not overridden + TimingParams updatedTiming = oldEffect->SpecifiedTiming(); + if (~aOverriddenProperties & CSSAnimationProperties::Duration) { + updatedTiming.SetDuration(aNewTiming.Duration()); + } + if (~aOverriddenProperties & CSSAnimationProperties::IterationCount) { + updatedTiming.SetIterations(aNewTiming.Iterations()); + } + if (~aOverriddenProperties & CSSAnimationProperties::Direction) { + updatedTiming.SetDirection(aNewTiming.Direction()); + } + if (~aOverriddenProperties & CSSAnimationProperties::Delay) { + updatedTiming.SetDelay(aNewTiming.Delay()); + } + if (~aOverriddenProperties & CSSAnimationProperties::FillMode) { + updatedTiming.SetFill(aNewTiming.Fill()); + } + + animationChanged = oldEffect->SpecifiedTiming() != updatedTiming; + oldEffect->SetSpecifiedTiming(std::move(updatedTiming)); + + if (KeyframeEffect* oldKeyframeEffect = oldEffect->AsKeyframeEffect()) { + if (~aOverriddenProperties & CSSAnimationProperties::Keyframes) { + aBuilder.SetKeyframes(*oldKeyframeEffect, std::move(aNewKeyframes), + aTimeline); + } + + if (~aOverriddenProperties & CSSAnimationProperties::Composition) { + animationChanged = oldKeyframeEffect->Composite() != aNewComposite; + oldKeyframeEffect->SetCompositeFromStyle(aNewComposite); + } + } + } + + // Checking pointers should be enough. If both are scroll-timeline, we reuse + // the scroll-timeline object if their scrollers and axes are the same. + if (aOld.GetTimeline() != aTimeline) { + aOld.SetTimeline(aTimeline); + animationChanged = true; + } + + // Handle changes in play state. If the animation is idle, however, + // changes to animation-play-state should *not* restart it. + if (aOld.PlayState() != AnimationPlayState::Idle && + ~aOverriddenProperties & CSSAnimationProperties::PlayState) { + bool wasPaused = aOld.PlayState() == AnimationPlayState::Paused; + if (!wasPaused && aNewIsStylePaused) { + aOld.PauseFromStyle(); + animationChanged = true; + } else if (wasPaused && !aNewIsStylePaused) { + aOld.PlayFromStyle(); + animationChanged = true; + } + } + + // Updating the effect timing above might already have caused the + // animation to become irrelevant so only add a changed record if + // the animation is still relevant. + if (animationChanged && aOld.IsRelevant()) { + MutationObservers::NotifyAnimationChanged(&aOld); + } +} + +static already_AddRefed GetNamedProgressTimeline( + dom::Document* aDocument, const NonOwningAnimationTarget& aTarget, + nsAtom* aName) { + // A named progress timeline is referenceable in animation-timeline by: + // 1. the declaring element itself + // 2. that element’s descendants + // 3. that element’s following siblings and their descendants + // https://drafts.csswg.org/scroll-animations-1/#timeline-scope + // FIXME: Bug 1823500. Reduce default scoping to ancestors only. + for (Element* curr = AnimationUtils::GetElementForRestyle( + aTarget.mElement, aTarget.mPseudoType); + curr; curr = curr->GetParentElement()) { + // If multiple elements have declared the same timeline name, the matching + // timeline is the one declared on the nearest element in tree order, which + // considers siblings closer than parents. + // Note: This is fine for parallel traversal because we update animations by + // SequentialTask. + for (Element* e = curr; e; e = e->GetPreviousElementSibling()) { + // In case of a name conflict on the same element, scroll progress + // timelines take precedence over view progress timelines. + const auto [element, pseudoType] = + AnimationUtils::GetElementPseudoPair(e); + if (auto* collection = + TimelineCollection::Get(element, pseudoType)) { + if (RefPtr timeline = collection->Lookup(aName)) { + return timeline.forget(); + } + } + + if (auto* collection = + TimelineCollection::Get(element, pseudoType)) { + if (RefPtr timeline = collection->Lookup(aName)) { + return timeline.forget(); + } + } + } + } + + // If we cannot find a matched scroll-timeline-name, this animation is not + // associated with a timeline. + // https://drafts.csswg.org/css-animations-2/#valdef-animation-timeline-custom-ident + return nullptr; +} + +static already_AddRefed GetTimeline( + const StyleAnimationTimeline& aStyleTimeline, nsPresContext* aPresContext, + const NonOwningAnimationTarget& aTarget) { + switch (aStyleTimeline.tag) { + case StyleAnimationTimeline::Tag::Timeline: { + // Check scroll-timeline-name property or view-timeline-property. + nsAtom* name = aStyleTimeline.AsTimeline().AsAtom(); + return name != nsGkAtoms::_empty + ? GetNamedProgressTimeline(aPresContext->Document(), aTarget, + name) + : nullptr; + } + case StyleAnimationTimeline::Tag::Scroll: { + const auto& scroll = aStyleTimeline.AsScroll(); + return ScrollTimeline::MakeAnonymous(aPresContext->Document(), aTarget, + scroll.axis, scroll.scroller); + } + case StyleAnimationTimeline::Tag::View: { + const auto& view = aStyleTimeline.AsView(); + return ViewTimeline::MakeAnonymous(aPresContext->Document(), aTarget, + view.axis, view.inset); + } + case StyleAnimationTimeline::Tag::Auto: + return do_AddRef(aTarget.mElement->OwnerDoc()->Timeline()); + } + MOZ_ASSERT_UNREACHABLE("Unknown animation-timeline value?"); + return nullptr; +} + +// Returns a new animation set up with given StyleAnimation. +// Or returns an existing animation matching StyleAnimation's name updated +// with the new StyleAnimation. +static already_AddRefed BuildAnimation( + nsPresContext* aPresContext, const NonOwningAnimationTarget& aTarget, + const nsStyleUIReset& aStyle, uint32_t animIdx, + ServoCSSAnimationBuilder& aBuilder, + nsAnimationManager::CSSAnimationCollection* aCollection) { + MOZ_ASSERT(aPresContext); + + nsAtom* animationName = aStyle.GetAnimationName(animIdx); + nsTArray keyframes; + if (!aBuilder.BuildKeyframes(*aTarget.mElement, aPresContext, animationName, + aStyle.GetAnimationTimingFunction(animIdx), + keyframes)) { + return nullptr; + } + + TimingParams timing = TimingParamsFromCSSParams( + aStyle.GetAnimationDuration(animIdx).ToMilliseconds(), + aStyle.GetAnimationDelay(animIdx).ToMilliseconds(), + aStyle.GetAnimationIterationCount(animIdx), + aStyle.GetAnimationDirection(animIdx), + aStyle.GetAnimationFillMode(animIdx)); + + bool isStylePaused = + aStyle.GetAnimationPlayState(animIdx) == StyleAnimationPlayState::Paused; + + RefPtr timeline = + GetTimeline(aStyle.GetTimeline(animIdx), aPresContext, aTarget); + + // Find the matching animation with animation name in the old list + // of animations and remove the matched animation from the list. + RefPtr oldAnim = + PopExistingAnimation(animationName, aCollection); + + const auto composition = StyleToDom(aStyle.GetAnimationComposition(animIdx)); + if (oldAnim) { + // Copy over the start times and (if still paused) pause starts + // for each animation (matching on name only) that was also in the + // old list of animations. + // This means that we honor dynamic changes, which isn't what the + // spec says to do, but WebKit seems to honor at least some of + // them. See + // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html + // In order to honor what the spec said, we'd copy more data over. + UpdateOldAnimationPropertiesWithNew( + *oldAnim, std::move(timing), std::move(keyframes), isStylePaused, + oldAnim->GetOverriddenProperties(), aBuilder, timeline, composition); + return oldAnim.forget(); + } + + KeyframeEffectParams effectOptions(composition); + RefPtr effect = new dom::CSSAnimationKeyframeEffect( + aPresContext->Document(), + OwningAnimationTarget(aTarget.mElement, aTarget.mPseudoType), + std::move(timing), effectOptions); + + aBuilder.SetKeyframes(*effect, std::move(keyframes), timeline); + + RefPtr animation = new CSSAnimation( + aPresContext->Document()->GetScopeObject(), animationName); + animation->SetOwningElement( + OwningElementRef(*aTarget.mElement, aTarget.mPseudoType)); + + animation->SetTimelineNoUpdate(timeline); + animation->SetEffectNoUpdate(effect); + + if (isStylePaused) { + animation->PauseFromStyle(); + } else { + animation->PlayFromStyle(); + } + + aBuilder.NotifyNewOrRemovedAnimation(*animation); + + return animation.forget(); +} + +static nsAnimationManager::OwningCSSAnimationPtrArray BuildAnimations( + nsPresContext* aPresContext, const NonOwningAnimationTarget& aTarget, + const nsStyleUIReset& aStyle, ServoCSSAnimationBuilder& aBuilder, + nsAnimationManager::CSSAnimationCollection* aCollection, + nsTHashSet>& aReferencedAnimations) { + nsAnimationManager::OwningCSSAnimationPtrArray result; + + for (size_t animIdx = aStyle.mAnimationNameCount; animIdx-- != 0;) { + nsAtom* name = aStyle.GetAnimationName(animIdx); + // CSS Animations whose animation-name does not match a @keyframes rule do + // not generate animation events. This includes when the animation-name is + // "none" which is represented by an empty name in the StyleAnimation. + // Since such animations neither affect style nor dispatch events, we do + // not generate a corresponding CSSAnimation for them. + if (name == nsGkAtoms::_empty) { + continue; + } + + aReferencedAnimations.Insert(name); + RefPtr dest = BuildAnimation(aPresContext, aTarget, aStyle, + animIdx, aBuilder, aCollection); + if (!dest) { + continue; + } + + dest->SetAnimationIndex(static_cast(animIdx)); + result.AppendElement(dest); + } + return result; +} + +void nsAnimationManager::UpdateAnimations(dom::Element* aElement, + PseudoStyleType aPseudoType, + const ComputedStyle* aComputedStyle) { + MOZ_ASSERT(mPresContext->IsDynamic(), + "Should not update animations for print or print preview"); + MOZ_ASSERT(aElement->IsInComposedDoc(), + "Should not update animations that are not attached to the " + "document tree"); + + if (!aComputedStyle || + aComputedStyle->StyleDisplay()->mDisplay == StyleDisplay::None) { + // If we are in a display:none subtree we will have no computed values. + // However, if we are on the root of display:none subtree, the computed + // values might not have been cleared yet. + // In either case, since CSS animations should not run in display:none + // subtrees we should stop (actually, destroy) any animations on this + // element here. + StopAnimationsForElement(aElement, aPseudoType); + return; + } + + NonOwningAnimationTarget target(aElement, aPseudoType); + ServoCSSAnimationBuilder builder(aComputedStyle); + + DoUpdateAnimations(target, *aComputedStyle->StyleUIReset(), builder); +} + +void nsAnimationManager::DoUpdateAnimations( + const NonOwningAnimationTarget& aTarget, const nsStyleUIReset& aStyle, + ServoCSSAnimationBuilder& aBuilder) { + // Everything that causes our animation data to change triggers a + // style change, which in turn triggers a non-animation restyle. + // Likewise, when we initially construct frames, we're not in a + // style change, but also not in an animation restyle. + + auto* collection = + CSSAnimationCollection::Get(aTarget.mElement, aTarget.mPseudoType); + if (!collection && aStyle.mAnimationNameCount == 1 && + aStyle.mAnimations[0].GetName() == nsGkAtoms::_empty) { + return; + } + + nsAutoAnimationMutationBatch mb(aTarget.mElement->OwnerDoc()); + + // Build the updated animations list, extracting matching animations from + // the existing collection as we go. + OwningCSSAnimationPtrArray newAnimations = + BuildAnimations(mPresContext, aTarget, aStyle, aBuilder, collection, + mMaybeReferencedAnimations); + + if (newAnimations.IsEmpty()) { + if (collection) { + collection->Destroy(); + } + return; + } + + if (!collection) { + collection = + &aTarget.mElement->EnsureAnimationData().EnsureAnimationCollection( + *aTarget.mElement, aTarget.mPseudoType); + if (!collection->isInList()) { + AddElementCollection(collection); + } + } + collection->mAnimations.SwapElements(newAnimations); + + // Cancel removed animations + for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0;) { + aBuilder.NotifyNewOrRemovedAnimation(*newAnimations[newAnimIdx]); + newAnimations[newAnimIdx]->CancelFromStyle(PostRestyleMode::IfNeeded); + } +} diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h new file mode 100644 index 0000000000..f37fa0a7d1 --- /dev/null +++ b/layout/style/nsAnimationManager.h @@ -0,0 +1,109 @@ +/* -*- 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 nsAnimationManager_h_ +#define nsAnimationManager_h_ + +#include "mozilla/Attributes.h" +#include "AnimationCommon.h" +#include "mozilla/dom/CSSAnimation.h" +#include "mozilla/Keyframe.h" +#include "mozilla/MemoryReporting.h" +#include "nsISupportsImpl.h" +#include "nsTHashSet.h" + +class ServoCSSAnimationBuilder; + +struct nsStyleUIReset; + +namespace mozilla { +class ComputedStyle; + +enum class PseudoStyleType : uint8_t; +struct NonOwningAnimationTarget; + +} /* namespace mozilla */ + +class nsAnimationManager final + : public mozilla::CommonAnimationManager { + public: + explicit nsAnimationManager(nsPresContext* aPresContext) + : mozilla::CommonAnimationManager( + aPresContext) {} + + typedef mozilla::AnimationCollection + CSSAnimationCollection; + typedef nsTArray> + OwningCSSAnimationPtrArray; + + ~nsAnimationManager() override = default; + + /** + * This function does the same thing as the above UpdateAnimations() + * but with servo's computed values. + */ + void UpdateAnimations(mozilla::dom::Element* aElement, + mozilla::PseudoStyleType aPseudoType, + const mozilla::ComputedStyle* aComputedValues); + + // Utility function to walk through |aIter| to find the Keyframe with + // matching offset and timing function but stopping as soon as the offset + // differs from |aOffset| (i.e. it assumes a sorted iterator). + // + // If a matching Keyframe is found, + // Returns true and sets |aIndex| to the index of the matching Keyframe + // within |aIter|. + // + // If no matching Keyframe is found, + // Returns false and sets |aIndex| to the index in the iterator of the + // first Keyframe with an offset differing to |aOffset| or, if the end + // of the iterator is reached, sets |aIndex| to the index after the last + // Keyframe. + template + static bool FindMatchingKeyframe( + IterType&& aIter, double aOffset, + const mozilla::StyleComputedTimingFunction& aTimingFunctionToMatch, + mozilla::dom::CompositeOperationOrAuto aCompositionToMatch, + size_t& aIndex) { + aIndex = 0; + for (mozilla::Keyframe& keyframe : aIter) { + if (keyframe.mOffset.value() != aOffset) { + break; + } + const bool matches = [&] { + if (keyframe.mComposite != aCompositionToMatch) { + return false; + } + return keyframe.mTimingFunction + ? *keyframe.mTimingFunction == aTimingFunctionToMatch + : aTimingFunctionToMatch.IsLinearKeyword(); + }(); + if (matches) { + return true; + } + ++aIndex; + } + return false; + } + + bool AnimationMayBeReferenced(nsAtom* aName) const { + return mMaybeReferencedAnimations.Contains(aName); + } + + private: + // This includes all animation names referenced regardless of whether a + // corresponding `@keyframes` rule is available. + // + // It may contain names which are no longer referenced, but it should always + // contain names which are currently referenced, so that it is usable for + // style invalidation. + nsTHashSet> mMaybeReferencedAnimations; + + void DoUpdateAnimations(const mozilla::NonOwningAnimationTarget& aTarget, + const nsStyleUIReset& aStyle, + ServoCSSAnimationBuilder& aBuilder); +}; + +#endif /* !defined(nsAnimationManager_h_) */ diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h new file mode 100644 index 0000000000..891b34814b --- /dev/null +++ b/layout/style/nsCSSAnonBoxList.h @@ -0,0 +1,163 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +/* + * This file contains the list of nsAtoms and their values for CSS + * pseudo-element-ish things used internally for anonymous boxes. It is + * designed to be used as inline input to nsCSSAnonBoxes.cpp *only* through the + * magic of C preprocessing. All entries must be enclosed in the macros + * CSS_ANON_BOX, CSS_WRAPPER_ANON_BOX, or CSS_NON_INHERITING_ANON_BOX which will + * have cruel and unusual things done to it. The entries should be kept in some + * sort of logical order. + * + * The first argument to + * CSS_ANON_BOX/CSS_WRAPPER_ANON_BOX/CSS_NON_INHERITING_ANON_BOX is the C++ + * identifier of the atom. + * + * The second argument is the string value of the atom. + * + * CSS_NON_INHERITING_ANON_BOX is used for anon boxes that never inherit style + * from anything. This means all their property values are the initial values + * of those properties. These ones must come first! Code relies on this. + * If this macro is not defined, it will default to CSS_ANON_BOX. + * + * CSS_WRAPPER_ANON_BOX is used for anon boxes that are used as wrappers around + * other frames during frametree fixup (e.g. table anonymous boxes, ruby + * anonymous boxes, anonymous flex item blocks, etc). These are also inheriting + * anon boxes, just like CSS_ANON_BOX. If this macro is not defined, it will + * default to CSS_ANON_BOX. + */ + +// OUTPUT_CLASS=nsCSSAnonBoxes +// MACRO_NAME=CSS_ANON_BOX/CSS_NON_INHERITING_ANON_BOX/CSS_WRAPPER_ANON_BOX + +#ifndef CSS_NON_INHERITING_ANON_BOX +# ifdef DEFINED_CSS_NON_INHERITING_ANON_BOX +# error "Recursive includes of nsCSSAnonBoxList.h?" +# endif /* DEFINED_CSS_NON_INHERITING_ANON_BOX */ +# define CSS_NON_INHERITING_ANON_BOX(name_, value_) CSS_ANON_BOX(name_, value_) +# define DEFINED_CSS_NON_INHERITING_ANON_BOX +#endif /* CSS_NON_INHERITING_ANON_BOX */ + +#ifndef CSS_WRAPPER_ANON_BOX +# ifdef DEFINED_CSS_WRAPPER_ANON_BOX +# error "Recursive includes of nsCSSAnonBoxList.h?" +# endif /* DEFINED_CSS_WRAPPER_ANON_BOX */ +# define CSS_WRAPPER_ANON_BOX(name_, value_) CSS_ANON_BOX(name_, value_) +# define DEFINED_CSS_WRAPPER_ANON_BOX +#endif /* CSS_WRAPPER_ANON_BOX */ + +//--------------------------------------------------------------------------- +// Non-inheriting ones, which must come first +//--------------------------------------------------------------------------- + +// Placeholder frames for out of flows. Note that :-moz-placeholder is used for +// the pseudo-element that represents the placeholder text in , so we need a different string here. +CSS_NON_INHERITING_ANON_BOX(oofPlaceholder, ":-moz-oof-placeholder") + +// Framesets +CSS_NON_INHERITING_ANON_BOX(horizontalFramesetBorder, ":-moz-hframeset-border") +CSS_NON_INHERITING_ANON_BOX(verticalFramesetBorder, ":-moz-vframeset-border") + +CSS_NON_INHERITING_ANON_BOX(framesetBlank, ":-moz-frameset-blank") + +CSS_NON_INHERITING_ANON_BOX(tableColGroup, ":-moz-table-column-group") +CSS_NON_INHERITING_ANON_BOX(tableCol, ":-moz-table-column") + +CSS_NON_INHERITING_ANON_BOX(page, ":-moz-page") +CSS_NON_INHERITING_ANON_BOX(pageBreak, ":-moz-page-break") +CSS_NON_INHERITING_ANON_BOX(pageContent, ":-moz-page-content") +CSS_NON_INHERITING_ANON_BOX(printedSheet, ":-moz-printed-sheet") + +// Applies to blocks that wrap contiguous runs of "column-span: all" +// elements in multi-column subtrees, or the wrappers themselves, all the +// way up to the column set wrappers. +CSS_NON_INHERITING_ANON_BOX(columnSpanWrapper, ":-moz-column-span-wrapper") + +//--------------------------------------------------------------------------- +// Other ones +//--------------------------------------------------------------------------- + +// ::-moz-text, ::-moz-oof-placeholder, and ::-moz-first-letter-continuation are +// non-elements which no rule will match. +CSS_ANON_BOX(mozText, ":-moz-text") +// nsFirstLetterFrames for content outside the ::first-letter. +CSS_ANON_BOX(firstLetterContinuation, ":-moz-first-letter-continuation") + +CSS_ANON_BOX(mozBlockInsideInlineWrapper, ":-moz-block-inside-inline-wrapper") +CSS_WRAPPER_ANON_BOX(mozMathMLAnonymousBlock, ":-moz-mathml-anonymous-block") + +CSS_ANON_BOX(mozLineFrame, ":-moz-line-frame") + +CSS_ANON_BOX(buttonContent, ":-moz-button-content") +CSS_ANON_BOX(cellContent, ":-moz-cell-content") +CSS_ANON_BOX(dropDownList, ":-moz-dropdown-list") +CSS_ANON_BOX(fieldsetContent, ":-moz-fieldset-content") +CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame") +CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content") + +CSS_WRAPPER_ANON_BOX(inlineTable, ":-moz-inline-table") +CSS_WRAPPER_ANON_BOX(table, ":-moz-table") +CSS_WRAPPER_ANON_BOX(tableCell, ":-moz-table-cell") +CSS_ANON_BOX(tableWrapper, ":-moz-table-wrapper") +CSS_WRAPPER_ANON_BOX(tableRowGroup, ":-moz-table-row-group") +CSS_WRAPPER_ANON_BOX(tableRow, ":-moz-table-row") + +CSS_ANON_BOX(canvas, ":-moz-canvas") +CSS_ANON_BOX(pageSequence, ":-moz-page-sequence") +CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content") +CSS_ANON_BOX(scrolledCanvas, ":-moz-scrolled-canvas") + +// A column set is a set of columns inside of ColumnSetWrapperFrame, which +// applies to nsColumnSetFrame. It doesn't contain any column-span elements. +CSS_ANON_BOX(columnSet, ":-moz-column-set") + +// Applies to each column block inside of a column set. +CSS_ANON_BOX(columnContent, ":-moz-column-content") + +CSS_ANON_BOX(viewport, ":-moz-viewport") +CSS_ANON_BOX(viewportScroll, ":-moz-viewport-scroll") + +// Inside a flex/grid/-moz-box container, a contiguous run of text gets wrapped +// in an anonymous block, which is then treated as a flex item. +CSS_WRAPPER_ANON_BOX(anonymousItem, ":-moz-anonymous-item") + +CSS_ANON_BOX(blockRubyContent, ":-moz-block-ruby-content") +CSS_WRAPPER_ANON_BOX(ruby, ":-moz-ruby") +CSS_WRAPPER_ANON_BOX(rubyBase, ":-moz-ruby-base") +CSS_WRAPPER_ANON_BOX(rubyBaseContainer, ":-moz-ruby-base-container") +CSS_WRAPPER_ANON_BOX(rubyText, ":-moz-ruby-text") +CSS_WRAPPER_ANON_BOX(rubyTextContainer, ":-moz-ruby-text-container") + +CSS_ANON_BOX(mozTreeColumn, ":-moz-tree-column") +CSS_ANON_BOX(mozTreeRow, ":-moz-tree-row") +CSS_ANON_BOX(mozTreeSeparator, ":-moz-tree-separator") +CSS_ANON_BOX(mozTreeCell, ":-moz-tree-cell") +CSS_ANON_BOX(mozTreeIndentation, ":-moz-tree-indentation") +CSS_ANON_BOX(mozTreeLine, ":-moz-tree-line") +CSS_ANON_BOX(mozTreeTwisty, ":-moz-tree-twisty") +CSS_ANON_BOX(mozTreeImage, ":-moz-tree-image") +CSS_ANON_BOX(mozTreeCellText, ":-moz-tree-cell-text") +CSS_ANON_BOX(mozTreeCheckbox, ":-moz-tree-checkbox") +CSS_ANON_BOX(mozTreeDropFeedback, ":-moz-tree-drop-feedback") + +CSS_ANON_BOX(mozSVGMarkerAnonChild, ":-moz-svg-marker-anon-child") +CSS_ANON_BOX(mozSVGOuterSVGAnonChild, ":-moz-svg-outer-svg-anon-child") +CSS_ANON_BOX(mozSVGForeignContent, ":-moz-svg-foreign-content") +CSS_ANON_BOX(mozSVGText, ":-moz-svg-text") + +#ifdef DEFINED_CSS_NON_INHERITING_ANON_BOX +# undef DEFINED_CSS_NON_INHERITING_ANON_BOX +# undef CSS_NON_INHERITING_ANON_BOX +#endif /* DEFINED_CSS_NON_INHERITING_ANON_BOX */ + +#ifdef DEFINED_CSS_WRAPPER_ANON_BOX +# undef DEFINED_CSS_WRAPPER_ANON_BOX +# undef CSS_WRAPPER_ANON_BOX +#endif /* DEFINED_CSS_NON_INHERITING_ANON_BOX */ diff --git a/layout/style/nsCSSAnonBoxes.cpp b/layout/style/nsCSSAnonBoxes.cpp new file mode 100644 index 0000000000..db9f183927 --- /dev/null +++ b/layout/style/nsCSSAnonBoxes.cpp @@ -0,0 +1,45 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCSSAnonBoxes.h" +#include "nsGkAtomConsts.h" +#include "nsStaticAtomUtils.h" + +using namespace mozilla; + +/* static */ +bool nsCSSAnonBoxes::IsTreePseudoElement(nsAtom* aPseudo) { + return StringBeginsWith(nsDependentAtomString(aPseudo), u":-moz-tree-"_ns); +} + +#ifdef DEBUG +/* static */ +nsStaticAtom* nsCSSAnonBoxes::GetAtomBase() { + return const_cast( + nsGkAtoms::GetAtomByIndex(kAtomIndex_AnonBoxes)); +} + +/* static */ +void nsCSSAnonBoxes::AssertAtoms() { + nsStaticAtom* base = GetAtomBase(); + size_t index = 0; +# define CSS_ANON_BOX(name_, value_) \ + { \ + RefPtr atom = NS_Atomize(value_); \ + MOZ_ASSERT(atom == nsGkAtoms::AnonBox_##name_, \ + "Static atom for " #name_ " has incorrect value"); \ + MOZ_ASSERT(atom == &base[index], \ + "Static atom for " #name_ " not at expected index"); \ + ++index; \ + } +# include "nsCSSAnonBoxList.h" +# undef CSS_ANON_BOX +} +#endif diff --git a/layout/style/nsCSSAnonBoxes.h b/layout/style/nsCSSAnonBoxes.h new file mode 100644 index 0000000000..78539b8d10 --- /dev/null +++ b/layout/style/nsCSSAnonBoxes.h @@ -0,0 +1,63 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +#ifndef nsCSSAnonBoxes_h___ +#define nsCSSAnonBoxes_h___ + +#include "nsAtom.h" +#include "nsGkAtoms.h" +#include "mozilla/PseudoStyleType.h" + +class nsCSSAnonBoxes { + using PseudoStyleType = mozilla::PseudoStyleType; + using PseudoStyle = mozilla::PseudoStyle; + + public: + static bool IsTreePseudoElement(nsAtom* aPseudo); + static bool IsNonElement(PseudoStyleType aPseudo) { + return aPseudo == PseudoStyleType::mozText || + aPseudo == PseudoStyleType::oofPlaceholder || + aPseudo == PseudoStyleType::firstLetterContinuation; + } + + enum class NonInheriting : uint8_t { +#define CSS_ANON_BOX(_name, _value) /* nothing */ +#define CSS_NON_INHERITING_ANON_BOX(_name, _value) _name, +#include "nsCSSAnonBoxList.h" +#undef CSS_NON_INHERITING_ANON_BOX +#undef CSS_ANON_BOX + _Count + }; + + // Get the NonInheriting type for a given pseudo tag. The pseudo tag must + // test true for IsNonInheritingAnonBox. + static NonInheriting NonInheritingTypeForPseudoType(PseudoStyleType aType) { + MOZ_ASSERT(PseudoStyle::IsNonInheritingAnonBox(aType)); + static_assert(sizeof(PseudoStyleType) == sizeof(uint8_t), ""); + return static_cast( + static_cast(aType) - + static_cast(PseudoStyleType::NonInheritingAnonBoxesStart)); + } + +#ifdef DEBUG + static nsStaticAtom* GetAtomBase(); + static void AssertAtoms(); +#endif + +// Alias nsCSSAnonBoxes::foo() to nsGkAtoms::AnonBox_foo. +#define CSS_ANON_BOX(name_, value_) \ + static nsCSSAnonBoxPseudoStaticAtom* name_() { \ + return const_cast( \ + static_cast( \ + nsGkAtoms::AnonBox_##name_)); \ + } +#include "nsCSSAnonBoxList.h" +#undef CSS_ANON_BOX +}; + +#endif /* nsCSSAnonBoxes_h___ */ diff --git a/layout/style/nsCSSCounterDescList.h b/layout/style/nsCSSCounterDescList.h new file mode 100644 index 0000000000..bad70d1be0 --- /dev/null +++ b/layout/style/nsCSSCounterDescList.h @@ -0,0 +1,16 @@ +/* -*- 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/. */ + +CSS_COUNTER_DESC(system, System) +CSS_COUNTER_DESC(symbols, Symbols) +CSS_COUNTER_DESC(additive-symbols, AdditiveSymbols) +CSS_COUNTER_DESC(negative, Negative) +CSS_COUNTER_DESC(prefix, Prefix) +CSS_COUNTER_DESC(suffix, Suffix) +CSS_COUNTER_DESC(range, Range) +CSS_COUNTER_DESC(pad, Pad) +CSS_COUNTER_DESC(fallback, Fallback) +CSS_COUNTER_DESC(speak-as, SpeakAs) diff --git a/layout/style/nsCSSFontDescList.h b/layout/style/nsCSSFontDescList.h new file mode 100644 index 0000000000..a82856e4b7 --- /dev/null +++ b/layout/style/nsCSSFontDescList.h @@ -0,0 +1,20 @@ +/* -*- 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/. */ + +CSS_FONT_DESC(font-family, Family) +CSS_FONT_DESC(font-style, Style) +CSS_FONT_DESC(font-weight, Weight) +CSS_FONT_DESC(font-stretch, Stretch) +CSS_FONT_DESC(src, Src) +CSS_FONT_DESC(unicode-range, UnicodeRange) +CSS_FONT_DESC(font-feature-settings, FontFeatureSettings) +CSS_FONT_DESC(font-variation-settings, FontVariationSettings) +CSS_FONT_DESC(font-language-override, FontLanguageOverride) +CSS_FONT_DESC(font-display, Display) +CSS_FONT_DESC(ascent-override, AscentOverride) +CSS_FONT_DESC(descent-override, DescentOverride) +CSS_FONT_DESC(line-gap-override, LineGapOverride) +CSS_FONT_DESC(size-adjust, SizeAdjust) diff --git a/layout/style/nsCSSPropertyID.h.in b/layout/style/nsCSSPropertyID.h.in new file mode 100644 index 0000000000..4c8f951fee --- /dev/null +++ b/layout/style/nsCSSPropertyID.h.in @@ -0,0 +1,87 @@ +/* -*- 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/. */ + +/* enum types for CSS properties and their values */ + +#ifndef nsCSSPropertyID_h___ +#define nsCSSPropertyID_h___ + +#include + +/* + Declare the enum list using the magic of preprocessing + enum values are "eCSSProperty_foo" (where foo is the property) + + To change the list of properties, see ServoCSSPropList.h + + */ +enum nsCSSPropertyID : int32_t { + eCSSProperty_UNKNOWN = -1, + +$property_ids + + // Some of the values below could probably overlap with each other + // if we had a need for them to do so. + + // Extra value to represent custom properties (--*). + eCSSPropertyExtra_variable, +}; + +// MOZ_DBG support is defined in nsCSSProps.h since it depends on +// nsCSSProps::GetStringValue + +const nsCSSPropertyID + eCSSProperty_COUNT_no_shorthands = $longhand_count; +const nsCSSPropertyID + eCSSProperty_COUNT = $shorthand_count; +const nsCSSPropertyID + eCSSProperty_COUNT_with_aliases = eCSSPropertyExtra_variable; + +namespace mozilla { + +template<> +inline PLDHashNumber +Hash(const nsCSSPropertyID& aValue) +{ + return uint32_t(aValue); +} + +} // namespace mozilla + +// The "descriptors" that can appear in a @font-face rule. +// They have the syntax of properties but different value rules. +enum nsCSSFontDesc { + eCSSFontDesc_UNKNOWN = -1, +#define CSS_FONT_DESC(name_, method_) eCSSFontDesc_##method_, +#include "nsCSSFontDescList.h" +#undef CSS_FONT_DESC + eCSSFontDesc_COUNT +}; + +// The "descriptors" that can appear in a @counter-style rule. +// They have the syntax of properties but different value rules. +enum nsCSSCounterDesc { + eCSSCounterDesc_UNKNOWN = -1, +#define CSS_COUNTER_DESC(name_, method_) eCSSCounterDesc_##method_, +#include "nsCSSCounterDescList.h" +#undef CSS_COUNTER_DESC + eCSSCounterDesc_COUNT +}; + +namespace mozilla { + +// FIXME: The underlying type of this enum should be uint8_t, but we can't do +// that because of https://bugs.llvm.org/show_bug.cgi?id=44228. +enum class CountedUnknownProperty : uint32_t { +#define COUNTED_UNKNOWN_PROPERTY(name_, method_) method_, +#include "mozilla/CountedUnknownProperties.h" +#undef COUNTED_UNKNOWN_PROPERTY + Count, +}; + +} // namespace mozilla + +#endif /* nsCSSPropertyID_h___ */ diff --git a/layout/style/nsCSSPropertyIDSet.h b/layout/style/nsCSSPropertyIDSet.h new file mode 100644 index 0000000000..18488815ee --- /dev/null +++ b/layout/style/nsCSSPropertyIDSet.h @@ -0,0 +1,304 @@ +/* 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/. */ + +/* bit vectors for sets of CSS properties */ + +#ifndef nsCSSPropertyIDSet_h__ +#define nsCSSPropertyIDSet_h__ + +#include +#include // for CHAR_BIT +#include + +#include "mozilla/ArrayUtils.h" +// For COMPOSITOR_ANIMATABLE_PROPERTY_LIST and +// COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH +#include "mozilla/AnimatedPropertyID.h" +#include "mozilla/CompositorAnimatableProperties.h" +#include "nsCSSProps.h" // For operator<< for nsCSSPropertyID +#include "nsCSSPropertyID.h" + +/** + * nsCSSPropertyIDSet maintains a set of non-shorthand CSS properties. In + * other words, for each longhand CSS property we support, it has a bit + * for whether that property is in the set. + */ +class nsCSSPropertyIDSet { + public: + constexpr nsCSSPropertyIDSet() : mProperties{0} {} + // auto-generated copy-constructor OK + + explicit constexpr nsCSSPropertyIDSet( + std::initializer_list aProperties) + : mProperties{0} { + for (auto property : aProperties) { + size_t p = property; + mProperties[p / kBitsInChunk] |= property_set_type(1) + << (p % kBitsInChunk); + } + } + + void AssertInSetRange(nsCSSPropertyID aProperty) const { + MOZ_DIAGNOSTIC_ASSERT( + 0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of bounds"); + } + + // Conversion of aProperty to |size_t| after AssertInSetRange + // lets the compiler generate significantly tighter code. + + void AddProperty(nsCSSPropertyID aProperty) { + AssertInSetRange(aProperty); + size_t p = aProperty; + mProperties[p / kBitsInChunk] |= property_set_type(1) << (p % kBitsInChunk); + } + + void RemoveProperty(nsCSSPropertyID aProperty) { + AssertInSetRange(aProperty); + size_t p = aProperty; + mProperties[p / kBitsInChunk] &= + ~(property_set_type(1) << (p % kBitsInChunk)); + } + + bool HasProperty(const mozilla::AnimatedPropertyID& aProperty) const { + return !aProperty.IsCustom() && HasProperty(aProperty.mID); + } + + bool HasProperty(nsCSSPropertyID aProperty) const { + AssertInSetRange(aProperty); + size_t p = aProperty; + return (mProperties[p / kBitsInChunk] & + (property_set_type(1) << (p % kBitsInChunk))) != 0; + } + + // Returns an nsCSSPropertyIDSet including all properties that can be run + // on the compositor. + static constexpr nsCSSPropertyIDSet CompositorAnimatables() { + return nsCSSPropertyIDSet(COMPOSITOR_ANIMATABLE_PROPERTY_LIST); + } + + static constexpr size_t CompositorAnimatableCount() { + return COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH; + } + + static constexpr size_t CompositorAnimatableDisplayItemCount() { + // We have 3 individual transforms and 5 motion path properties, and they + // also use DisplayItemType::TYPE_TRANSFORM. + return COMPOSITOR_ANIMATABLE_PROPERTY_LIST_LENGTH - 8; + } + + static constexpr nsCSSPropertyIDSet CSSTransformProperties() { + return nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_translate, + eCSSProperty_rotate, eCSSProperty_scale}; + } + + static constexpr nsCSSPropertyIDSet MotionPathProperties() { + return nsCSSPropertyIDSet{ + eCSSProperty_offset_path, eCSSProperty_offset_distance, + eCSSProperty_offset_rotate, eCSSProperty_offset_anchor, + eCSSProperty_offset_position}; + } + + static constexpr nsCSSPropertyIDSet TransformLikeProperties() { + return nsCSSPropertyIDSet{ + eCSSProperty_transform, eCSSProperty_translate, + eCSSProperty_rotate, eCSSProperty_scale, + eCSSProperty_offset_path, eCSSProperty_offset_distance, + eCSSProperty_offset_rotate, eCSSProperty_offset_anchor, + eCSSProperty_offset_position}; + } + + static constexpr nsCSSPropertyIDSet OpacityProperties() { + return nsCSSPropertyIDSet{eCSSProperty_opacity}; + } + + bool Intersects(const nsCSSPropertyIDSet& aOther) const { + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + if (mProperties[i] & aOther.mProperties[i]) { + return true; + } + } + return false; + } + + void Empty() { memset(mProperties, 0, sizeof(mProperties)); } + + void AssertIsEmpty(const char* aText) const { + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + NS_ASSERTION(mProperties[i] == 0, aText); + } + } + + bool Equals(const nsCSSPropertyIDSet& aOther) const { + return mozilla::ArrayEqual(mProperties, aOther.mProperties); + } + + bool IsEmpty() const { + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + if (mProperties[i] != 0) { + return false; + } + } + return true; + } + + bool IsSubsetOf(const nsCSSPropertyIDSet& aOther) const { + return this->Intersect(aOther).Equals(*this); + } + + // Return a new nsCSSPropertyIDSet which is the inverse of this set. + nsCSSPropertyIDSet Inverse() const { + nsCSSPropertyIDSet result; + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + result.mProperties[i] = ~mProperties[i]; + } + return result; + } + + // Returns a new nsCSSPropertyIDSet with all properties that are both in + // this set and |aOther|. + nsCSSPropertyIDSet Intersect(const nsCSSPropertyIDSet& aOther) const { + nsCSSPropertyIDSet result; + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + result.mProperties[i] = mProperties[i] & aOther.mProperties[i]; + } + return result; + } + + // Return a new nsCSSPropertyIDSet with all properties that are in either + // this set or |aOther| but not both. + nsCSSPropertyIDSet Xor(const nsCSSPropertyIDSet& aOther) const { + nsCSSPropertyIDSet result; + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + result.mProperties[i] = mProperties[i] ^ aOther.mProperties[i]; + } + return result; + } + + nsCSSPropertyIDSet& operator|=(const nsCSSPropertyIDSet& aOther) { + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + mProperties[i] |= aOther.mProperties[i]; + } + return *this; + } + + private: + typedef unsigned long property_set_type; + + public: + // number of bits in |property_set_type|. + static const size_t kBitsInChunk = sizeof(property_set_type) * CHAR_BIT; + // number of |property_set_type|s in the set + static const size_t kChunkCount = + (eCSSProperty_COUNT_no_shorthands + kBitsInChunk - 1) / kBitsInChunk; + + /* + * For fast enumeration of all the bits that are set, callers can + * check each chunk against zero (since in normal cases few bits are + * likely to be set). + */ + bool HasPropertyInChunk(size_t aChunk) const { + return mProperties[aChunk] != 0; + } + bool HasPropertyAt(size_t aChunk, size_t aBit) const { + return (mProperties[aChunk] & (property_set_type(1) << aBit)) != 0; + } + static nsCSSPropertyID CSSPropertyAt(size_t aChunk, size_t aBit) { + return nsCSSPropertyID(aChunk * kBitsInChunk + aBit); + } + + // Iterator for use in range-based for loops + class Iterator { + public: + Iterator(Iterator&& aOther) + : mPropertySet(aOther.mPropertySet), + mChunk(aOther.mChunk), + mBit(aOther.mBit) {} + + static Iterator BeginIterator(const nsCSSPropertyIDSet& aPropertySet) { + Iterator result(aPropertySet); + + // Search for the first property. + // Unsigned integer overflow is defined so the following is safe. + result.mBit = -1; + ++result; + + return result; + } + + static Iterator EndIterator(const nsCSSPropertyIDSet& aPropertySet) { + Iterator result(aPropertySet); + result.mChunk = kChunkCount; + result.mBit = 0; + return result; + } + + bool operator!=(const Iterator& aOther) const { + return mChunk != aOther.mChunk || mBit != aOther.mBit; + } + + Iterator& operator++() { + MOZ_ASSERT(mChunk < kChunkCount, "Should not iterate beyond end"); + + do { + mBit++; + } while (mBit < kBitsInChunk && + !mPropertySet.HasPropertyAt(mChunk, mBit)); + if (mBit != kBitsInChunk) { + return *this; + } + + do { + mChunk++; + } while (mChunk < kChunkCount && + !mPropertySet.HasPropertyInChunk(mChunk)); + mBit = 0; + if (mChunk != kChunkCount) { + while (mBit < kBitsInChunk && + !mPropertySet.HasPropertyAt(mChunk, mBit)) { + mBit++; + } + } + + return *this; + } + + nsCSSPropertyID operator*() { + MOZ_ASSERT(mChunk < kChunkCount, "Should not dereference beyond end"); + return nsCSSPropertyIDSet::CSSPropertyAt(mChunk, mBit); + } + + private: + explicit Iterator(const nsCSSPropertyIDSet& aPropertySet) + : mPropertySet(aPropertySet) {} + + Iterator() = delete; + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + Iterator& operator=(const Iterator&&) = delete; + + const nsCSSPropertyIDSet& mPropertySet; + size_t mChunk = 0; + size_t mBit = 0; + }; + + Iterator begin() const { return Iterator::BeginIterator(*this); } + Iterator end() const { return Iterator::EndIterator(*this); } + + private: + property_set_type mProperties[kChunkCount]; +}; + +// MOZ_DBG support + +inline std::ostream& operator<<(std::ostream& aOut, + const nsCSSPropertyIDSet& aPropertySet) { + AutoTArray properties; + for (nsCSSPropertyID property : aPropertySet) { + properties.AppendElement(property); + } + return aOut << properties; +} + +#endif /* !defined(nsCSSPropertyIDSet_h__) */ diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp new file mode 100644 index 0000000000..5f2f275d6e --- /dev/null +++ b/layout/style/nsCSSProps.cpp @@ -0,0 +1,247 @@ +/* -*- 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/. */ + +/* + * methods for dealing with CSS properties and tables of the keyword + * values they accept + */ + +#include "nsCSSProps.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Casting.h" + +#include "gfxPlatform.h" +#include "nsLayoutUtils.h" +#include "nsIWidget.h" +#include "nsStyleConsts.h" // For system widget appearance types + +#include "mozilla/dom/Animation.h" +#include "mozilla/dom/AnimationEffectBinding.h" // for PlaybackDirection +#include "mozilla/gfx/gfxVars.h" // for UseWebRender +#include "mozilla/gfx/gfxVarReceiver.h" +#include "mozilla/LookAndFeel.h" // for system colors + +#include "nsString.h" +#include "nsStaticNameTable.h" + +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/ClearOnShutdown.h" + +using namespace mozilla; + +static StaticAutoPtr gFontDescTable; +static StaticAutoPtr gCounterDescTable; +static StaticAutoPtr> + gPropertyIDLNameTable; + +static constexpr const char* const kCSSRawFontDescs[] = { +#define CSS_FONT_DESC(name_, method_) #name_, +#include "nsCSSFontDescList.h" +#undef CSS_FONT_DESC +}; + +static constexpr const char* const kCSSRawCounterDescs[] = { +#define CSS_COUNTER_DESC(name_, method_) #name_, +#include "nsCSSCounterDescList.h" +#undef CSS_COUNTER_DESC +}; + +static constexpr CSSPropFlags kFlagsTable[eCSSProperty_COUNT_with_aliases] = { +#define CSS_PROP_LONGHAND(name_, id_, method_, flags_, ...) flags_, +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, ...) flags_, +#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, flags_, ...) flags_, +#include "mozilla/ServoCSSPropList.h" +#undef CSS_PROP_ALIAS +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP_LONGHAND +}; + +static nsStaticCaseInsensitiveNameTable* CreateStaticTable( + const char* const aRawTable[], int32_t aLength) { + auto* table = new nsStaticCaseInsensitiveNameTable(aRawTable, aLength); +#ifdef DEBUG + // Partially verify the entries. + for (int32_t index = 0; index < aLength; ++index) { + nsAutoCString temp(aRawTable[index]); + MOZ_ASSERT(-1 == temp.FindChar('_'), + "underscore char in case insensitive name table"); + } +#endif + return table; +} + +void nsCSSProps::RecomputeEnabledState(const char* aPref, void*) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + DebugOnly foundPref = false; + for (const PropertyPref* pref = kPropertyPrefTable; + pref->mPropID != eCSSProperty_UNKNOWN; pref++) { + if (!aPref || !strcmp(aPref, pref->mPref)) { + foundPref = true; +#ifdef FUZZING + gPropertyEnabled[pref->mPropID] = true; +#else + gPropertyEnabled[pref->mPropID] = Preferences::GetBool(pref->mPref); + if (pref->mPropID == eCSSProperty_backdrop_filter) { + gPropertyEnabled[pref->mPropID] &= + gfx::gfxVars::GetAllowBackdropFilterOrDefault(); + } +#endif + } + } + MOZ_ASSERT(foundPref); +} + +void nsCSSProps::Init() { + MOZ_ASSERT(!gFontDescTable, "pre existing array!"); + MOZ_ASSERT(!gCounterDescTable, "pre existing array!"); + MOZ_ASSERT(!gPropertyIDLNameTable, "pre existing array!"); + + gFontDescTable = CreateStaticTable(kCSSRawFontDescs, eCSSFontDesc_COUNT); + gCounterDescTable = + CreateStaticTable(kCSSRawCounterDescs, eCSSCounterDesc_COUNT); + + gPropertyIDLNameTable = new nsTHashMap; + for (nsCSSPropertyID p = nsCSSPropertyID(0); + size_t(p) < ArrayLength(kIDLNameTable); p = nsCSSPropertyID(p + 1)) { + if (kIDLNameTable[p]) { + gPropertyIDLNameTable->InsertOrUpdate( + nsDependentCString(kIDLNameTable[p]), p); + } + } + + ClearOnShutdown(&gFontDescTable); + ClearOnShutdown(&gCounterDescTable); + ClearOnShutdown(&gPropertyIDLNameTable); + + for (const PropertyPref* pref = kPropertyPrefTable; + pref->mPropID != eCSSProperty_UNKNOWN; pref++) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1472523 + // We need to use nsCString instead of substring because the preference + // callback code stores them. Using AssignLiteral prevents any + // unnecessary allocations. + nsCString prefName; + prefName.AssignLiteral(pref->mPref, strlen(pref->mPref)); + Preferences::RegisterCallback(nsCSSProps::RecomputeEnabledState, prefName); + } + RecomputeEnabledState(/* aPrefName = */ nullptr); +} + +/* static */ +bool nsCSSProps::IsCustomPropertyName(const nsACString& aProperty) { + return aProperty.Length() >= CSS_CUSTOM_NAME_PREFIX_LENGTH && + StringBeginsWith(aProperty, "--"_ns); +} + +nsCSSPropertyID nsCSSProps::LookupPropertyByIDLName( + const nsACString& aPropertyIDLName, EnabledState aEnabled) { + MOZ_ASSERT(gPropertyIDLNameTable, "no lookup table, needs addref"); + nsCSSPropertyID res; + if (!gPropertyIDLNameTable->Get(aPropertyIDLName, &res)) { + return eCSSProperty_UNKNOWN; + } + MOZ_ASSERT(res < eCSSProperty_COUNT); + if (!IsEnabled(res, aEnabled)) { + return eCSSProperty_UNKNOWN; + } + return res; +} + +nsCSSFontDesc nsCSSProps::LookupFontDesc(const nsACString& aFontDesc) { + MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref"); + nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc)); + + return which; +} + +static constexpr auto sDescNullStr = ""_ns; + +const nsCString& nsCSSProps::GetStringValue(nsCSSFontDesc aFontDescID) { + MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref"); + if (gFontDescTable) { + return gFontDescTable->GetStringValue(int32_t(aFontDescID)); + } + return sDescNullStr; +} + +const nsCString& nsCSSProps::GetStringValue(nsCSSCounterDesc aCounterDescID) { + MOZ_ASSERT(gCounterDescTable, "no lookup table, needs addref"); + if (gCounterDescTable) { + return gCounterDescTable->GetStringValue(int32_t(aCounterDescID)); + } + return sDescNullStr; +} + +CSSPropFlags nsCSSProps::PropFlags(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases, + "out of range"); + return kFlagsTable[aProperty]; +} + +/* static */ +bool nsCSSProps::gPropertyEnabled[eCSSProperty_COUNT_with_aliases] = { +// If the property has any "ENABLED_IN" flag set, it is disabled by +// default. Note that, if a property has pref, whatever its default +// value is, it will later be changed in nsCSSProps::AddRefTable(). +// If the property has "ENABLED_IN" flags but doesn't have a pref, +// it is an internal property which is disabled elsewhere. +#define IS_ENABLED_BY_DEFAULT(flags_) \ + (!((flags_) & (CSSPropFlags::EnabledMask | CSSPropFlags::Inaccessible))) + +#define CSS_PROP_LONGHAND(name_, id_, method_, flags_, ...) \ + IS_ENABLED_BY_DEFAULT(flags_), +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, ...) \ + IS_ENABLED_BY_DEFAULT(flags_), +#define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, flags_, ...) \ + IS_ENABLED_BY_DEFAULT(flags_), +#include "mozilla/ServoCSSPropList.h" +#undef CSS_PROP_ALIAS +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP_LONGHAND + +#undef IS_ENABLED_BY_DEFAULT +}; + +/** + * A singleton class to register as a receiver for gfxVars. + * Updates the state of backdrop-filter's pref if the gfx + * backdrop filter var changes state. + */ +class nsCSSPropsGfxVarReceiver final : public gfx::gfxVarReceiver { + constexpr nsCSSPropsGfxVarReceiver() = default; + + // Backdrop filter's last known enabled state. + static bool sLastKnownAllowBackdropFilter; + static nsCSSPropsGfxVarReceiver sInstance; + + public: + static gfx::gfxVarReceiver& GetInstance() { return sInstance; } + + void OnVarChanged(const gfx::GfxVarUpdate&) override { + bool enabled = gfx::gfxVars::AllowBackdropFilter(); + if (sLastKnownAllowBackdropFilter != enabled) { + sLastKnownAllowBackdropFilter = enabled; + nsCSSProps::RecomputeEnabledState( + StaticPrefs::GetPrefName_layout_css_backdrop_filter_enabled()); + } + } +}; + +/* static */ +nsCSSPropsGfxVarReceiver nsCSSPropsGfxVarReceiver::sInstance = + nsCSSPropsGfxVarReceiver(); + +/* static */ +bool nsCSSPropsGfxVarReceiver::sLastKnownAllowBackdropFilter = true; + +/* static */ +gfx::gfxVarReceiver& nsCSSProps::GfxVarReceiver() { + return nsCSSPropsGfxVarReceiver::GetInstance(); +} + +#include "nsCSSPropsGenerated.inc" diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h new file mode 100644 index 0000000000..70cf78e6d5 --- /dev/null +++ b/layout/style/nsCSSProps.h @@ -0,0 +1,230 @@ +/* -*- 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/. */ + +/* + * methods for dealing with CSS properties and tables of the keyword + * values they accept + */ + +#ifndef nsCSSProps_h___ +#define nsCSSProps_h___ + +#include + +#include "nsString.h" +#include "nsCSSPropertyID.h" +#include "nsStyleStructFwd.h" +#include "mozilla/UseCounter.h" +#include "mozilla/CSSEnabledState.h" +#include "mozilla/CSSPropFlags.h" +#include "mozilla/Preferences.h" + +// Length of the "--" prefix on custom names (such as custom property names, +// and, in the future, custom media query names). +#define CSS_CUSTOM_NAME_PREFIX_LENGTH 2 + +namespace mozilla { +class ComputedStyle; +namespace gfx { +class gfxVarReceiver; +} +} // namespace mozilla + +extern "C" { +nsCSSPropertyID Servo_ResolveLogicalProperty(nsCSSPropertyID, + const mozilla::ComputedStyle*); +nsCSSPropertyID Servo_Property_LookupEnabledForAllContent(const nsACString*); +const uint8_t* Servo_Property_GetName(nsCSSPropertyID, uint32_t* aLength); +} + +class nsCSSProps { + public: + using EnabledState = mozilla::CSSEnabledState; + using Flags = mozilla::CSSPropFlags; + + static void Init(); + + // Looks up the property with name aProperty and returns its corresponding + // nsCSSPropertyID value. If aProperty is the name of a custom property, + // then eCSSPropertyExtra_variable will be returned. + // + // This only returns properties enabled for all content, and resolves aliases + // to return the aliased property. + static nsCSSPropertyID LookupProperty(const nsACString& aProperty) { + return Servo_Property_LookupEnabledForAllContent(&aProperty); + } + + // As above, but looked up using a property's IDL name. + // eCSSPropertyExtra_variable won't be returned from this method. + static nsCSSPropertyID LookupPropertyByIDLName( + const nsACString& aPropertyIDLName, EnabledState aEnabled); + + // Returns whether aProperty is a custom property name, i.e. begins with + // "--". This assumes that the CSS Variables pref has been enabled. + static bool IsCustomPropertyName(const nsACString& aProperty); + + static bool IsShorthand(nsCSSPropertyID aProperty) { + if (aProperty == eCSSPropertyExtra_variable) { + return false; + } + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return aProperty >= eCSSProperty_COUNT_no_shorthands; + } + + // Same but for @font-face descriptors + static nsCSSFontDesc LookupFontDesc(const nsACString&); + + // The relevant invariants are asserted in Document.cpp + static mozilla::UseCounter UseCounterFor(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases, + "out of range"); + return mozilla::UseCounter(size_t(mozilla::eUseCounter_FirstCSSProperty) + + size_t(aProperty)); + } + + // Given a property enum, get the string value + // + // This string is static. + static nsDependentCSubstring GetStringValue(nsCSSPropertyID aProperty) { + uint32_t len; + const uint8_t* chars = Servo_Property_GetName(aProperty, &len); + return nsDependentCSubstring(reinterpret_cast(chars), len); + } + + static const nsCString& GetStringValue(nsCSSFontDesc aFontDesc); + static const nsCString& GetStringValue(nsCSSCounterDesc aCounterDesc); + + static Flags PropFlags(nsCSSPropertyID); + static bool PropHasFlags(nsCSSPropertyID aProperty, Flags aFlags) { + return (PropFlags(aProperty) & aFlags) == aFlags; + } + + static nsCSSPropertyID Physicalize(nsCSSPropertyID aProperty, + const mozilla::ComputedStyle& aStyle) { + MOZ_ASSERT(!IsShorthand(aProperty)); + if (PropHasFlags(aProperty, Flags::IsLogical)) { + return Servo_ResolveLogicalProperty(aProperty, &aStyle); + } + return aProperty; + } + + private: + // A table for shorthand properties. The appropriate index is the + // property ID minus eCSSProperty_COUNT_no_shorthands. + static const nsCSSPropertyID* const + kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands]; + + public: + /** + * Returns true if the backdrop-filter pref and the gfx blocklist are enabled. + */ + static bool IsBackdropFilterAvailable(JSContext*, JSObject*) { + return IsEnabled(eCSSProperty_backdrop_filter, EnabledState::ForAllContent); + } + + /** + * Recoumputes the enabled state of a pref. If aPrefName is nullptr, + * recomputes the state of all prefs in gPropertyEnabled. + * aClosure is the pref callback closure data, which is not used. + */ + static void RecomputeEnabledState(const char* aPrefName, + void* aClosure = nullptr); + + /** + * Retrieve a singleton receiver to register with gfxVars + */ + static mozilla::gfx::gfxVarReceiver& GfxVarReceiver(); + + static const nsCSSPropertyID* SubpropertyEntryFor(nsCSSPropertyID aProperty) { + MOZ_ASSERT(eCSSProperty_COUNT_no_shorthands <= aProperty && + aProperty < eCSSProperty_COUNT, + "out of range"); + return kSubpropertyTable[aProperty - eCSSProperty_COUNT_no_shorthands]; + } + + private: + static bool gPropertyEnabled[eCSSProperty_COUNT_with_aliases]; + // Defined in the generated nsCSSPropsGenerated.inc. + static const char* const kIDLNameTable[eCSSProperty_COUNT]; + static const int32_t kIDLNameSortPositionTable[eCSSProperty_COUNT]; + + public: + /** + * Returns the IDL name of the specified property, which must be a + * longhand, logical or shorthand property. The IDL name is the property + * name with any hyphen-lowercase character pairs replaced by an + * uppercase character: + * https://drafts.csswg.org/cssom/#css-property-to-idl-attribute + * + * As a special case, the string "cssFloat" is returned for the float + * property. nullptr is returned for internal properties. + */ + static const char* PropertyIDLName(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return kIDLNameTable[aProperty]; + } + + /** + * Returns the position of the specified property in a list of all + * properties sorted by their IDL name. + */ + static int32_t PropertyIDLNameSortPosition(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return kIDLNameSortPositionTable[aProperty]; + } + + static bool IsEnabled(nsCSSPropertyID aProperty, EnabledState aEnabled) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases, + "out of range"); + // In the child process, assert that we're not trying to parse stylesheets + // before we've gotten all our prefs. + MOZ_ASSERT_IF(!XRE_IsParentProcess(), + mozilla::Preferences::ArePrefsInitedInContentProcess()); + if (gPropertyEnabled[aProperty]) { + return true; + } + if (aEnabled == EnabledState::IgnoreEnabledState) { + return true; + } + if ((aEnabled & EnabledState::InUASheets) && + PropHasFlags(aProperty, Flags::EnabledInUASheets)) { + return true; + } + if ((aEnabled & EnabledState::InChrome) && + PropHasFlags(aProperty, Flags::EnabledInChrome)) { + return true; + } + return false; + } + + struct PropertyPref { + nsCSSPropertyID mPropID; + const char* mPref; + }; + static const PropertyPref kPropertyPrefTable[]; + +// Storing the enabledstate_ value in an nsCSSPropertyID variable is a small +// hack to avoid needing a separate variable declaration for its real type +// (CSSEnabledState), which would then require using a block and +// therefore a pair of macros by consumers for the start and end of the loop. +#define CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(it_, prop_, enabledstate_) \ + for (const nsCSSPropertyID * \ + it_ = nsCSSProps::SubpropertyEntryFor(prop_), \ + es_ = (nsCSSPropertyID)((enabledstate_) | CSSEnabledState(0)); \ + *it_ != eCSSProperty_UNKNOWN; ++it_) \ + if (nsCSSProps::IsEnabled(*it_, (mozilla::CSSEnabledState)es_)) +}; + +// MOZ_DBG support for nsCSSPropertyID + +inline std::ostream& operator<<(std::ostream& aOut, nsCSSPropertyID aProperty) { + return aOut << nsCSSProps::GetStringValue(aProperty); +} + +#endif /* nsCSSProps_h___ */ diff --git a/layout/style/nsCSSPseudoElementList.h b/layout/style/nsCSSPseudoElementList.h new file mode 100644 index 0000000000..9514dd6c4b --- /dev/null +++ b/layout/style/nsCSSPseudoElementList.h @@ -0,0 +1,109 @@ +/* -*- 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/. */ + +/* list of CSS pseudo-elements */ + +/* + * This file contains the list of support CSS pseudo-elements and some flags. + * It is designed to be used as inline input to nsCSSPseudoElements.cpp *only* + * through the magic of C preprocessing. All entries must be enclosed either + * in the macro CSS_PSEUDO_ELEMENT; these macros will have cruel and unusual + * things done to them. The entries should be kept in some sort of logical + * order. + * + * Code including this file MUST define CSS_PSEUDO_ELEMENT, which takes + * three parameters: + * name_ : The C++ identifier used for the atom (which will be a member + * of nsCSSPseudoElements) + * value_ : The pseudo-element as a string, with single-colon syntax, + * used as the string value of the atom. + * flags_ : A bitfield containing flags defined in nsCSSPseudoElements.h + * + * A corresponding atom must also be defined in StaticAtoms.py with a name of + * "PseudoElement_" and whose value matches the definition in this file. + */ + +// OUTPUT_CLASS=nsCSSPseudoElements +// MACRO_NAME=CSS_PSEUDO_ELEMENT + +CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2 | + CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) +CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2 | + CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) +CSS_PSEUDO_ELEMENT(marker, ":marker", 0) + +CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0) + +CSS_PSEUDO_ELEMENT(cue, ":cue", CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC | + CSS_PSEUDO_ELEMENT_SUPPORTS_STYLE_ATTRIBUTE) + +CSS_PSEUDO_ELEMENT(firstLetter, ":first-letter", + CSS_PSEUDO_ELEMENT_IS_CSS2 | + CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS) +CSS_PSEUDO_ELEMENT(firstLine, ":first-line", + CSS_PSEUDO_ELEMENT_IS_CSS2 | + CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS) +CSS_PSEUDO_ELEMENT(highlight, ":highlight", 0) + +CSS_PSEUDO_ELEMENT(selection, ":selection", + CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS) + +// XXXbz should we really allow random content to style these? Maybe +// use our flags to prevent that? +CSS_PSEUDO_ELEMENT(mozFocusInner, ":-moz-focus-inner", 0) + +// HTML5 Forms pseudo elements +CSS_PSEUDO_ELEMENT(mozNumberSpinBox, ":-moz-number-spin-box", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) +CSS_PSEUDO_ELEMENT(mozNumberSpinUp, ":-moz-number-spin-up", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) +CSS_PSEUDO_ELEMENT(mozNumberSpinDown, ":-moz-number-spin-down", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) +CSS_PSEUDO_ELEMENT(mozSearchClearButton, ":-moz-search-clear-button", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) +CSS_PSEUDO_ELEMENT(mozProgressBar, ":-moz-progress-bar", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(mozRangeTrack, ":-moz-range-track", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(mozRangeProgress, ":-moz-range-progress", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(mozRangeThumb, ":-moz-range-thumb", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(mozMeterBar, ":-moz-meter-bar", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(placeholder, ":placeholder", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +CSS_PSEUDO_ELEMENT(mozColorSwatch, ":-moz-color-swatch", + CSS_PSEUDO_ELEMENT_SUPPORTS_STYLE_ATTRIBUTE | + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) +// The root of the text value anonymous content inside an or + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_exposed_prop_accessors.html b/layout/style/test/test_exposed_prop_accessors.html new file mode 100644 index 0000000000..765818bae4 --- /dev/null +++ b/layout/style/test/test_exposed_prop_accessors.html @@ -0,0 +1,41 @@ + + + + + Test for cloning of CSS property values (including 'inherit', 'initial' and 'unset') + + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_extra_inherit_initial.html b/layout/style/test/test_extra_inherit_initial.html new file mode 100644 index 0000000000..34c63b3626 --- /dev/null +++ b/layout/style/test/test_extra_inherit_initial.html @@ -0,0 +1,109 @@ + + + + + Test handling extra inherit/initial/unset in CSS declarations (Bug 940229) + + + + + +Mozilla Bug 940229 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_first_letter_restrictions.html b/layout/style/test/test_first_letter_restrictions.html new file mode 100644 index 0000000000..99aaba8b93 --- /dev/null +++ b/layout/style/test/test_first_letter_restrictions.html @@ -0,0 +1,56 @@ + + + + + + Test for Bug 1382786 + + + + + + +Mozilla Bug 1382786 +

    + +
    +
    + + + diff --git a/layout/style/test/test_first_line_restrictions.html b/layout/style/test/test_first_line_restrictions.html new file mode 100644 index 0000000000..bb8e116e8b --- /dev/null +++ b/layout/style/test/test_first_line_restrictions.html @@ -0,0 +1,56 @@ + + + + + + Test for Bug 1382786 + + + + + + +Mozilla Bug 1382786 +

    + +
    +
    + + + diff --git a/layout/style/test/test_flexbox_child_display_values.xhtml b/layout/style/test/test_flexbox_child_display_values.xhtml new file mode 100644 index 0000000000..7ba870c69f --- /dev/null +++ b/layout/style/test/test_flexbox_child_display_values.xhtml @@ -0,0 +1,178 @@ + + + + + Test "display" values of content in a flex container (Bug 783415) + + + + +Mozilla Bug 783415 +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_flex_grow_and_shrink.html b/layout/style/test/test_flexbox_flex_grow_and_shrink.html new file mode 100644 index 0000000000..fc5090fd61 --- /dev/null +++ b/layout/style/test/test_flexbox_flex_grow_and_shrink.html @@ -0,0 +1,154 @@ + + + + + + Test for flex-grow and flex-shrink animation (Bug 696253) + + + + + + +Mozilla Bug 696253 +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_flex_shorthand.html b/layout/style/test/test_flexbox_flex_shorthand.html new file mode 100644 index 0000000000..b8416403b6 --- /dev/null +++ b/layout/style/test/test_flexbox_flex_shorthand.html @@ -0,0 +1,280 @@ + + + + + + Test for Bug 696253 + + + + + +Mozilla Bug 696253 +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_focus_order.html b/layout/style/test/test_flexbox_focus_order.html new file mode 100644 index 0000000000..0c1f023e3c --- /dev/null +++ b/layout/style/test/test_flexbox_focus_order.html @@ -0,0 +1,83 @@ + + + + + Test for Bug 812687: focus order of reordered flex items + + + + + + +Mozilla Bug 812687 +

    + Link before container + +

    + 1 + + + + 5 +
    +

    + + +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_layout.html b/layout/style/test/test_flexbox_layout.html new file mode 100644 index 0000000000..49ee287aa2 --- /dev/null +++ b/layout/style/test/test_flexbox_layout.html @@ -0,0 +1,184 @@ + + + + + + Test for Bug 666041 + + + + + + +Mozilla Bug 666041 +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_order.html b/layout/style/test/test_flexbox_order.html new file mode 100644 index 0000000000..64b5431da8 --- /dev/null +++ b/layout/style/test/test_flexbox_order.html @@ -0,0 +1,194 @@ + + + + + + Test for Bug 666041 + + + + + + +Mozilla Bug 666041 +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_order_abspos.html b/layout/style/test/test_flexbox_order_abspos.html new file mode 100644 index 0000000000..bf4c99aa76 --- /dev/null +++ b/layout/style/test/test_flexbox_order_abspos.html @@ -0,0 +1,217 @@ + + + + + + Test for Bug 1345873 + + + + + + +Mozilla Bug 1345873 +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_order_table.html b/layout/style/test/test_flexbox_order_table.html new file mode 100644 index 0000000000..2423d5d6d6 --- /dev/null +++ b/layout/style/test/test_flexbox_order_table.html @@ -0,0 +1,198 @@ + + + + + + Test for Bug 799775 + + + + + + +Mozilla Bug 799775 +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flexbox_reflow_counts.html b/layout/style/test/test_flexbox_reflow_counts.html new file mode 100644 index 0000000000..a8f4913f7d --- /dev/null +++ b/layout/style/test/test_flexbox_reflow_counts.html @@ -0,0 +1,199 @@ + + + + + + Test for Bug 1142686 + + + + + +Mozilla Bug 1142686 +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_flushing_frame.html b/layout/style/test/test_flushing_frame.html new file mode 100644 index 0000000000..fa6d751647 --- /dev/null +++ b/layout/style/test/test_flushing_frame.html @@ -0,0 +1,40 @@ + + + + Test for bug 1545516: We don't flush layout unnecessarily on the parent + document when the frame is already disconnected. + + + +
    + diff --git a/layout/style/test/test_font_face_cascade.html b/layout/style/test/test_font_face_cascade.html new file mode 100644 index 0000000000..0d98f9d606 --- /dev/null +++ b/layout/style/test/test_font_face_cascade.html @@ -0,0 +1,35 @@ + +Test that @font-face rules from different origins cascade correctly + + + diff --git a/layout/style/test/test_font_face_parser.html b/layout/style/test/test_font_face_parser.html new file mode 100644 index 0000000000..448db4ff14 --- /dev/null +++ b/layout/style/test/test_font_face_parser.html @@ -0,0 +1,386 @@ + + + + + Test of @font-face parser + + + + +

    @font-face parsing (bug 441469)

    +
    
    +
    +
    +
    +
    diff --git a/layout/style/test/test_font_family_parsing.html b/layout/style/test/test_font_family_parsing.html
    new file mode 100644
    index 0000000000..59bedc0b94
    --- /dev/null
    +++ b/layout/style/test/test_font_family_parsing.html
    @@ -0,0 +1,272 @@
    +
    +
    +
    +  
    +  Font family name parsing tests
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    
    +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_font_family_serialization.html b/layout/style/test/test_font_family_serialization.html
    new file mode 100644
    index 0000000000..ad922158f5
    --- /dev/null
    +++ b/layout/style/test/test_font_family_serialization.html
    @@ -0,0 +1,51 @@
    +
    +
    +
    +
    +
    +
    +
    + diff --git a/layout/style/test/test_font_loading_api.html b/layout/style/test/test_font_loading_api.html new file mode 100644 index 0000000000..7b2a0d9e1b --- /dev/null +++ b/layout/style/test/test_font_loading_api.html @@ -0,0 +1,1895 @@ + + +Test for the CSS Font Loading API + + + + + + + + + + + + +
    diff --git a/layout/style/test/test_garbage_at_end_of_declarations.html b/layout/style/test/test_garbage_at_end_of_declarations.html new file mode 100644 index 0000000000..95882d1591 --- /dev/null +++ b/layout/style/test/test_garbage_at_end_of_declarations.html @@ -0,0 +1,156 @@ + + + + + Test handling of garbage at the end of CSS declarations + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_grid_computed_values.html b/layout/style/test/test_grid_computed_values.html new file mode 100644 index 0000000000..68a183606c --- /dev/null +++ b/layout/style/test/test_grid_computed_values.html @@ -0,0 +1,113 @@ + + + + + Test computed grid values + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + diff --git a/layout/style/test/test_grid_container_shorthands.html b/layout/style/test/test_grid_container_shorthands.html new file mode 100644 index 0000000000..1b7a434208 --- /dev/null +++ b/layout/style/test/test_grid_container_shorthands.html @@ -0,0 +1,271 @@ + + + + + Test parsing of grid container shorthands (grid-template, grid) + + + + + + + + + + diff --git a/layout/style/test/test_grid_item_shorthands.html b/layout/style/test/test_grid_item_shorthands.html new file mode 100644 index 0000000000..a50be6112d --- /dev/null +++ b/layout/style/test/test_grid_item_shorthands.html @@ -0,0 +1,153 @@ + + + + + Test parsing of grid item shorthands (grid-column, grid-row, grid-area) + + + + + + + + + + + diff --git a/layout/style/test/test_grid_shorthand_serialization.html b/layout/style/test/test_grid_shorthand_serialization.html new file mode 100644 index 0000000000..b2d32b9364 --- /dev/null +++ b/layout/style/test/test_grid_shorthand_serialization.html @@ -0,0 +1,221 @@ + + + + + Test serialization of CSS 'grid' shorthand property + + + + + + + + + + diff --git a/layout/style/test/test_group_insertRule.html b/layout/style/test/test_group_insertRule.html new file mode 100644 index 0000000000..85edc2a1a0 --- /dev/null +++ b/layout/style/test/test_group_insertRule.html @@ -0,0 +1,243 @@ + + + + CSS Variables Allowed Syntax + + + + + + + + + + +
    +
    + + + + diff --git a/layout/style/test/test_hover_on_part.html b/layout/style/test/test_hover_on_part.html new file mode 100644 index 0000000000..fc39dcc307 --- /dev/null +++ b/layout/style/test/test_hover_on_part.html @@ -0,0 +1,52 @@ + +Shadow parts are invalidated correctly when only a pseudo-class state to the right of the part matches + + + +
    +
    + diff --git a/layout/style/test/test_hover_quirk.html b/layout/style/test/test_hover_quirk.html new file mode 100644 index 0000000000..61e19f2a60 --- /dev/null +++ b/layout/style/test/test_hover_quirk.html @@ -0,0 +1,118 @@ + + + + + Test for the :active and :hover quirk + + + + + + + + Mozilla Bug 783213 +

    +
    +
    +
    +
    + Span
    +
    + Link
    +
    Div

    +
    +
    
    +
    +
    diff --git a/layout/style/test/test_html_attribute_computed_values.html b/layout/style/test/test_html_attribute_computed_values.html
    new file mode 100644
    index 0000000000..74e5b9754a
    --- /dev/null
    +++ b/layout/style/test/test_html_attribute_computed_values.html
    @@ -0,0 +1,84 @@
    +
    +
    +
    +
    +  Test for Bug 
    +  
    +  
    +
    +
    +Mozilla Bug 
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_ident_escaping.html b/layout/style/test/test_ident_escaping.html new file mode 100644 index 0000000000..d727e7f207 --- /dev/null +++ b/layout/style/test/test_ident_escaping.html @@ -0,0 +1,56 @@ + + + + + Test for Bug 543428 + + + + + + +Mozilla Bug 543428 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_img_src_causing_reflow.html b/layout/style/test/test_img_src_causing_reflow.html new file mode 100644 index 0000000000..e63b39f9fe --- /dev/null +++ b/layout/style/test/test_img_src_causing_reflow.html @@ -0,0 +1,36 @@ + + +Test for bug 1787072 + + + + diff --git a/layout/style/test/test_import_preload.html b/layout/style/test/test_import_preload.html new file mode 100644 index 0000000000..1c095c9768 --- /dev/null +++ b/layout/style/test/test_import_preload.html @@ -0,0 +1,22 @@ + + + + + + + diff --git a/layout/style/test/test_inherit_computation.html b/layout/style/test/test_inherit_computation.html new file mode 100644 index 0000000000..9ac056518c --- /dev/null +++ b/layout/style/test/test_inherit_computation.html @@ -0,0 +1,176 @@ + + + + + Test for computation of CSS 'inherit' on all properties and 'unset' on inherited properties + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_inherit_storage.html b/layout/style/test/test_inherit_storage.html new file mode 100644 index 0000000000..a9587d34d9 --- /dev/null +++ b/layout/style/test/test_inherit_storage.html @@ -0,0 +1,120 @@ + + + + + Test for parsing, storage, and serialization of CSS 'inherit' on all properties and 'unset' on inherited properties + + + + + +Mozilla Bug 375363 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_initial_computation.html b/layout/style/test/test_initial_computation.html new file mode 100644 index 0000000000..c8a36af227 --- /dev/null +++ b/layout/style/test/test_initial_computation.html @@ -0,0 +1,159 @@ + + + + + Test for computation of CSS 'initial' on all properties and 'unset' on reset properties + + + + + + + + +

    + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_initial_storage.html b/layout/style/test/test_initial_storage.html new file mode 100644 index 0000000000..a1a081c5a6 --- /dev/null +++ b/layout/style/test/test_initial_storage.html @@ -0,0 +1,134 @@ + + + + + Test for parsing, storage, and serialization of CSS 'initial' on all properties and 'unset' on reset properties + + + + + +Mozilla Bug 375363 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_invalidation_basic.html b/layout/style/test/test_invalidation_basic.html new file mode 100644 index 0000000000..b5a6928405 --- /dev/null +++ b/layout/style/test/test_invalidation_basic.html @@ -0,0 +1,45 @@ + + + + Test for bug 1368240: We only invalidate style as little as needed + + + + +
    +
    +
    +
    +
    +
    +
    + diff --git a/layout/style/test/test_keyframes_rules.html b/layout/style/test/test_keyframes_rules.html new file mode 100644 index 0000000000..027a406009 --- /dev/null +++ b/layout/style/test/test_keyframes_rules.html @@ -0,0 +1,134 @@ + + + + + Test for Bug 577974 + + + + + +Mozilla Bug 577974 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_keyframes_vendor_prefix.html b/layout/style/test/test_keyframes_vendor_prefix.html new file mode 100644 index 0000000000..4463bd259a --- /dev/null +++ b/layout/style/test/test_keyframes_vendor_prefix.html @@ -0,0 +1,167 @@ + + + +Test for interaction between prefixed and non-prefixed @keyframes rules with +the same name + + + +
    + diff --git a/layout/style/test/test_load_events_on_stylesheets.html b/layout/style/test/test_load_events_on_stylesheets.html new file mode 100644 index 0000000000..7344e27b22 --- /dev/null +++ b/layout/style/test/test_load_events_on_stylesheets.html @@ -0,0 +1,176 @@ + + + + + Test for Bug 185236 + + + + + + +Mozilla Bug 185236 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_logical_properties.html b/layout/style/test/test_logical_properties.html new file mode 100644 index 0000000000..a6947791cb --- /dev/null +++ b/layout/style/test/test_logical_properties.html @@ -0,0 +1,422 @@ + + +Test for handling of logical and physical properties + + + + + + + +
    + + + diff --git a/layout/style/test/test_marker_restrictions.html b/layout/style/test/test_marker_restrictions.html new file mode 100644 index 0000000000..f547f928cb --- /dev/null +++ b/layout/style/test/test_marker_restrictions.html @@ -0,0 +1,35 @@ + + +Test for ::marker property restrictions. + + + + +
    +
    + diff --git a/layout/style/test/test_mask_image_CORS.html b/layout/style/test/test_mask_image_CORS.html new file mode 100644 index 0000000000..8edd8af48e --- /dev/null +++ b/layout/style/test/test_mask_image_CORS.html @@ -0,0 +1,63 @@ + + + + +Test mask-image CORS anonymous retrieval + + + + + + + + + +

    There should be a green square, but no red square.

    +
    +
    + + diff --git a/layout/style/test/test_media_queries.html b/layout/style/test/test_media_queries.html new file mode 100644 index 0000000000..441fc1105a --- /dev/null +++ b/layout/style/test/test_media_queries.html @@ -0,0 +1,867 @@ + + + + + Test for Bug 156716 + + + + + +Mozilla Bug 156716 + + +
    +
    +
    + + diff --git a/layout/style/test/test_media_queries_dynamic.html b/layout/style/test/test_media_queries_dynamic.html new file mode 100644 index 0000000000..52e5fda9ca --- /dev/null +++ b/layout/style/test/test_media_queries_dynamic.html @@ -0,0 +1,207 @@ + + + + + Test for Bug 473400 + + + + +Mozilla Bug 473400 + + +
    +
    +
    + + + diff --git a/layout/style/test/test_media_query_list.html b/layout/style/test/test_media_query_list.html new file mode 100644 index 0000000000..5f213117e5 --- /dev/null +++ b/layout/style/test/test_media_query_list.html @@ -0,0 +1,373 @@ + + + + + Test for MediaQueryList (Bug 542058) + + + + +Mozilla Bug 542058 + + +
    +
    +
    + + diff --git a/layout/style/test/test_media_query_serialization.html b/layout/style/test/test_media_query_serialization.html new file mode 100644 index 0000000000..ed82653db0 --- /dev/null +++ b/layout/style/test/test_media_query_serialization.html @@ -0,0 +1,53 @@ + + + +Test media query list serialization + + + + + + + + diff --git a/layout/style/test/test_moz_device_pixel_ratio.html b/layout/style/test/test_moz_device_pixel_ratio.html new file mode 100644 index 0000000000..0e3e143fe8 --- /dev/null +++ b/layout/style/test/test_moz_device_pixel_ratio.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 474356 + + + + + + +Mozilla Bug 474356 + + +
    +
    +
    +
    + +
    + + diff --git a/layout/style/test/test_moz_prefixed_cursor.html b/layout/style/test/test_moz_prefixed_cursor.html new file mode 100644 index 0000000000..db7ffaaf56 --- /dev/null +++ b/layout/style/test/test_moz_prefixed_cursor.html @@ -0,0 +1,14 @@ + +Cursor aliases compute to the unprefixed keyword + + +
    + diff --git a/layout/style/test/test_mq_any_hover_and_any_pointer.html b/layout/style/test/test_mq_any_hover_and_any_pointer.html new file mode 100644 index 0000000000..1725af0725 --- /dev/null +++ b/layout/style/test/test_mq_any_hover_and_any_pointer.html @@ -0,0 +1,97 @@ + + + + + + Test for Bug 1035774 + + + + +Mozilla Bug 1483111 +

    + +
    +
    + + + diff --git a/layout/style/test/test_mq_changes_in_iframe.html b/layout/style/test/test_mq_changes_in_iframe.html new file mode 100644 index 0000000000..3a36476c42 --- /dev/null +++ b/layout/style/test/test_mq_changes_in_iframe.html @@ -0,0 +1,54 @@ + + + + + Media feature value change propagation in an iframe + + + + +

    + + +
    
    +
    +
    +
    diff --git a/layout/style/test/test_mq_hover_and_pointer.html b/layout/style/test/test_mq_hover_and_pointer.html
    new file mode 100644
    index 0000000000..40eaed5d0b
    --- /dev/null
    +++ b/layout/style/test/test_mq_hover_and_pointer.html
    @@ -0,0 +1,127 @@
    +
    +
    +
    +
    +  
    +  Test for Bug 1035774
    +  
    +  
    +
    +
    +Mozilla Bug 1035774
    +

    + +
    +
    + + + diff --git a/layout/style/test/test_mq_prefers_contrast_dynamic.html b/layout/style/test/test_mq_prefers_contrast_dynamic.html new file mode 100644 index 0000000000..c3ccebbe82 --- /dev/null +++ b/layout/style/test/test_mq_prefers_contrast_dynamic.html @@ -0,0 +1,82 @@ + + + + + + Test for Bug 1691793 + + + + +Mozilla Bug 1691793 +

    + +
    +
    + + + diff --git a/layout/style/test/test_mq_prefers_reduced_motion_dynamic.html b/layout/style/test/test_mq_prefers_reduced_motion_dynamic.html new file mode 100644 index 0000000000..570c0d3954 --- /dev/null +++ b/layout/style/test/test_mq_prefers_reduced_motion_dynamic.html @@ -0,0 +1,86 @@ + + + + + + Test for Bug 1478519 + + + + +Mozilla Bug 1486971 +

    + +
    +
    + + + diff --git a/layout/style/test/test_mql_event_listener_leaks.html b/layout/style/test/test_mql_event_listener_leaks.html new file mode 100644 index 0000000000..3ceb5412a2 --- /dev/null +++ b/layout/style/test/test_mql_event_listener_leaks.html @@ -0,0 +1,43 @@ + + + + + Bug 1450271 - Test MediaQueryList event listener leak conditions + + + + + + + + + + diff --git a/layout/style/test/test_namespace_rule.html b/layout/style/test/test_namespace_rule.html new file mode 100644 index 0000000000..6e8ba52c90 --- /dev/null +++ b/layout/style/test/test_namespace_rule.html @@ -0,0 +1,461 @@ + + + + Test for CSS Namespace rules + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_non_content_accessible_env_vars.html b/layout/style/test/test_non_content_accessible_env_vars.html new file mode 100644 index 0000000000..d9714216b7 --- /dev/null +++ b/layout/style/test/test_non_content_accessible_env_vars.html @@ -0,0 +1,39 @@ + + + + + + diff --git a/layout/style/test/test_non_content_accessible_properties.html b/layout/style/test/test_non_content_accessible_properties.html new file mode 100644 index 0000000000..220ddf2d26 --- /dev/null +++ b/layout/style/test/test_non_content_accessible_properties.html @@ -0,0 +1,85 @@ + + + + + + diff --git a/layout/style/test/test_non_content_accessible_pseudos.html b/layout/style/test/test_non_content_accessible_pseudos.html new file mode 100644 index 0000000000..81493b1a4a --- /dev/null +++ b/layout/style/test/test_non_content_accessible_pseudos.html @@ -0,0 +1,69 @@ + + + + + diff --git a/layout/style/test/test_non_content_accessible_values.html b/layout/style/test/test_non_content_accessible_values.html new file mode 100644 index 0000000000..633427f8e1 --- /dev/null +++ b/layout/style/test/test_non_content_accessible_values.html @@ -0,0 +1,172 @@ + + + + +
    + diff --git a/layout/style/test/test_non_matching_sheet_media.html b/layout/style/test/test_non_matching_sheet_media.html new file mode 100644 index 0000000000..a57444df45 --- /dev/null +++ b/layout/style/test/test_non_matching_sheet_media.html @@ -0,0 +1,30 @@ + + +Test for Bug 1386840: non-matching media list doesn't block rendering + + + + + + + diff --git a/layout/style/test/test_of_type_selectors.xhtml b/layout/style/test/test_of_type_selectors.xhtml new file mode 100644 index 0000000000..edf2e6ee97 --- /dev/null +++ b/layout/style/test/test_of_type_selectors.xhtml @@ -0,0 +1,97 @@ + + + + Test for *-of-type selectors in Bug 75375 + + + + +Mozilla Bug 75375 + +
    +
    +
    + + diff --git a/layout/style/test/test_overscroll_behavior_pref.html b/layout/style/test/test_overscroll_behavior_pref.html new file mode 100644 index 0000000000..c12d4ce081 --- /dev/null +++ b/layout/style/test/test_overscroll_behavior_pref.html @@ -0,0 +1,24 @@ + + + + + Test pref for overscroll-behavior property + + + + diff --git a/layout/style/test/test_page_parser.html b/layout/style/test/test_page_parser.html new file mode 100644 index 0000000000..6f6860d0f2 --- /dev/null +++ b/layout/style/test/test_page_parser.html @@ -0,0 +1,93 @@ + + + + + + Test of @page parser + + + + +

    @page parsing (bug 115199)

    +
    
    +
    +
    +
    +
    diff --git a/layout/style/test/test_parse_eof.html b/layout/style/test/test_parse_eof.html
    new file mode 100644
    index 0000000000..63ef95b980
    --- /dev/null
    +++ b/layout/style/test/test_parse_eof.html
    @@ -0,0 +1,69 @@
    +
    +
    +
    +  
    +  Test parsing behaviour of backslash just before EOF
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + diff --git a/layout/style/test/test_parse_ident.html b/layout/style/test/test_parse_ident.html new file mode 100644 index 0000000000..390412e2fc --- /dev/null +++ b/layout/style/test/test_parse_ident.html @@ -0,0 +1,56 @@ + + + + Test for CSS identifier parsing + + + + +Mozilla Bug + +
    +
    +
    + + diff --git a/layout/style/test/test_parse_rule.html b/layout/style/test/test_parse_rule.html new file mode 100644 index 0000000000..0fb1cc80ed --- /dev/null +++ b/layout/style/test/test_parse_rule.html @@ -0,0 +1,261 @@ + + + + + + + + + +
    + diff --git a/layout/style/test/test_parse_url.html b/layout/style/test/test_parse_url.html new file mode 100644 index 0000000000..7a637c18e3 --- /dev/null +++ b/layout/style/test/test_parse_url.html @@ -0,0 +1,195 @@ + + + + + Test for Bug 473914 + + + + +Mozilla Bug 473914 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_parser_diagnostics_unprintables.html b/layout/style/test/test_parser_diagnostics_unprintables.html new file mode 100644 index 0000000000..f872c00a8f --- /dev/null +++ b/layout/style/test/test_parser_diagnostics_unprintables.html @@ -0,0 +1,220 @@ + + + + + Test for CSS parser diagnostics escaping unprintable + characters correctly + + + + +Mozilla Bug 229827 + + + + diff --git a/layout/style/test/test_pixel_lengths.html b/layout/style/test/test_pixel_lengths.html new file mode 100644 index 0000000000..346547507c --- /dev/null +++ b/layout/style/test/test_pixel_lengths.html @@ -0,0 +1,61 @@ + + + + Test that pixel lengths don't change based on DPI + + + + +
    + +
    pt
    +
    pc
    +
    mm
    +
    cm
    +
    in
    +
    q
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_placeholder_restrictions.html b/layout/style/test/test_placeholder_restrictions.html new file mode 100644 index 0000000000..4e3e87ef16 --- /dev/null +++ b/layout/style/test/test_placeholder_restrictions.html @@ -0,0 +1,57 @@ + + + + + + Test for Bug 1382786 + + + + + + +Mozilla Bug 1382786 +

    + + + + + + diff --git a/layout/style/test/test_pointer-events.html b/layout/style/test/test_pointer-events.html new file mode 100644 index 0000000000..faeb6c6335 --- /dev/null +++ b/layout/style/test/test_pointer-events.html @@ -0,0 +1,114 @@ + + + + Test for pointer-events in HTML + + + + + + + +Mozilla Bug +
    + +
    +
    +
    +
    +
    + link + + + + + + + + + + + + +
    noyesyes
    noyesyes
    + + + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_position_float_display.html b/layout/style/test/test_position_float_display.html new file mode 100644 index 0000000000..ee75144dcb --- /dev/null +++ b/layout/style/test/test_position_float_display.html @@ -0,0 +1,111 @@ + + + + + + Test for Bug 1038929 + + + + + + +Mozilla Bug 1038929 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_position_sticky.html b/layout/style/test/test_position_sticky.html new file mode 100644 index 0000000000..b542737e54 --- /dev/null +++ b/layout/style/test/test_position_sticky.html @@ -0,0 +1,89 @@ + + + + + + Test for Bug 886646 + + + + + +Mozilla Bug 886646 +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_prefers_contrast_color_pairs.html b/layout/style/test/test_prefers_contrast_color_pairs.html new file mode 100644 index 0000000000..f4a8945804 --- /dev/null +++ b/layout/style/test/test_prefers_contrast_color_pairs.html @@ -0,0 +1,49 @@ + +Test for Bug 922669 + + + + diff --git a/layout/style/test/test_priority_preservation.html b/layout/style/test/test_priority_preservation.html new file mode 100644 index 0000000000..7177949555 --- /dev/null +++ b/layout/style/test/test_priority_preservation.html @@ -0,0 +1,141 @@ + + + + Test for property priority preservation + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_property_database.html b/layout/style/test/test_property_database.html new file mode 100644 index 0000000000..ba04ec341a --- /dev/null +++ b/layout/style/test/test_property_database.html @@ -0,0 +1,173 @@ + + + + + Test that property_database.js contains all supported CSS properties + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_property_syntax_errors.html b/layout/style/test/test_property_syntax_errors.html new file mode 100644 index 0000000000..1e3f382036 --- /dev/null +++ b/layout/style/test/test_property_syntax_errors.html @@ -0,0 +1,155 @@ + + + + + Test that we reject syntax errors listed in property_database.js + + + + + +

    + + +
    +
    +
    + + diff --git a/layout/style/test/test_pseudo_display_fixup.html b/layout/style/test/test_pseudo_display_fixup.html new file mode 100644 index 0000000000..de4dd0f810 --- /dev/null +++ b/layout/style/test/test_pseudo_display_fixup.html @@ -0,0 +1,29 @@ + + +Test item blockification of pseudo-elements + + + +
    + diff --git a/layout/style/test/test_pseudoelement_parsing.html b/layout/style/test/test_pseudoelement_parsing.html new file mode 100644 index 0000000000..b6fcf783f7 --- /dev/null +++ b/layout/style/test/test_pseudoelement_parsing.html @@ -0,0 +1,43 @@ + +Test for Bug 922669 + + + + + + + + diff --git a/layout/style/test/test_pseudoelement_state.html b/layout/style/test/test_pseudoelement_state.html new file mode 100644 index 0000000000..0a8c3d52f6 --- /dev/null +++ b/layout/style/test/test_pseudoelement_state.html @@ -0,0 +1,185 @@ + +Test for Bug 922669 + + + + + + + + diff --git a/layout/style/test/test_query_container_for.html b/layout/style/test/test_query_container_for.html new file mode 100644 index 0000000000..90dca9fab9 --- /dev/null +++ b/layout/style/test/test_query_container_for.html @@ -0,0 +1,62 @@ + +Test for bug 1789191 + + + + +
    + +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_redundant_font_download.html b/layout/style/test/test_redundant_font_download.html new file mode 100644 index 0000000000..c6930ae401 --- /dev/null +++ b/layout/style/test/test_redundant_font_download.html @@ -0,0 +1,131 @@ + + + + + + Test for bug 879963 + + + + + + + + + + + Mozilla Bug 879963 + +
    + + +
    + +
    + Test +
    + +
    + +
    + + + + + + diff --git a/layout/style/test/test_reframe_cb.html b/layout/style/test/test_reframe_cb.html new file mode 100644 index 0000000000..549d04c5c0 --- /dev/null +++ b/layout/style/test/test_reframe_cb.html @@ -0,0 +1,56 @@ + + + + Test for bug 1519371: We don't reframe for changes that affect our containing + block status unless whether we're a containing block really changed. + + + +
    + diff --git a/layout/style/test/test_reframe_image_loading.html b/layout/style/test/test_reframe_image_loading.html new file mode 100644 index 0000000000..2f8a44c361 --- /dev/null +++ b/layout/style/test/test_reframe_image_loading.html @@ -0,0 +1,29 @@ + + + + Test for bug 1395964: We don't reframe for image loading state changes. + + + +
    + diff --git a/layout/style/test/test_reframe_input.html b/layout/style/test/test_reframe_input.html new file mode 100644 index 0000000000..2887548abf --- /dev/null +++ b/layout/style/test/test_reframe_input.html @@ -0,0 +1,48 @@ + + +Test for bug 1658302: We don't reframe for placeholder attribute value changes. + + + + + diff --git a/layout/style/test/test_reframe_pseudo_element.html b/layout/style/test/test_reframe_pseudo_element.html new file mode 100644 index 0000000000..0e0b418e81 --- /dev/null +++ b/layout/style/test/test_reframe_pseudo_element.html @@ -0,0 +1,46 @@ + + + + Test for bug 1376352: We don't reframe all the time a replaced element that + matches generated content rules. + + + + + +
    + diff --git a/layout/style/test/test_rem_unit.html b/layout/style/test/test_rem_unit.html new file mode 100644 index 0000000000..bd46524798 --- /dev/null +++ b/layout/style/test/test_rem_unit.html @@ -0,0 +1,80 @@ + + + + + Test for CSS 'rem' unit + + + + +Mozilla Bug 478321 +

    +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_restyle_table_wrapper.html b/layout/style/test/test_restyle_table_wrapper.html new file mode 100644 index 0000000000..d8049b142e --- /dev/null +++ b/layout/style/test/test_restyle_table_wrapper.html @@ -0,0 +1,33 @@ + + + + Test for bug 1371955: We don't incorrectly think that a table wrapper style + is the main table element style. + + + + +
    + + diff --git a/layout/style/test/test_restyles_in_smil_animation.html b/layout/style/test/test_restyles_in_smil_animation.html new file mode 100644 index 0000000000..17c1ebb2ab --- /dev/null +++ b/layout/style/test/test_restyles_in_smil_animation.html @@ -0,0 +1,137 @@ + + + +Tests restyles in smil animation + + + + + + +
    + + + +
    + + + diff --git a/layout/style/test/test_revert.html b/layout/style/test/test_revert.html new file mode 100644 index 0000000000..48897a75ca --- /dev/null +++ b/layout/style/test/test_revert.html @@ -0,0 +1,103 @@ + +Test for computation of CSS 'revert' on all properties + + + + +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_root_node_display.html b/layout/style/test/test_root_node_display.html new file mode 100644 index 0000000000..54dd9c222c --- /dev/null +++ b/layout/style/test/test_root_node_display.html @@ -0,0 +1,74 @@ + + + + + + Test for Bug 969460 + + + + + +Mozilla Bug 969460 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_rule_insertion.html b/layout/style/test/test_rule_insertion.html new file mode 100644 index 0000000000..e3103309aa --- /dev/null +++ b/layout/style/test/test_rule_insertion.html @@ -0,0 +1,240 @@ + + + + + Test for Bug 816720 + + + + + + +
    
    +
    +

    ........

    +

    ........

    +

    ........

    + + + +

    +

    +

    + + + + + + diff --git a/layout/style/test/test_rules_out_of_sheets.html b/layout/style/test/test_rules_out_of_sheets.html new file mode 100644 index 0000000000..2ca53d31b7 --- /dev/null +++ b/layout/style/test/test_rules_out_of_sheets.html @@ -0,0 +1,115 @@ + + + + + Test for Bug 634373 + + + + +Mozilla Bug 634373 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_selectors.html b/layout/style/test/test_selectors.html new file mode 100644 index 0000000000..d688139d3c --- /dev/null +++ b/layout/style/test/test_selectors.html @@ -0,0 +1,1348 @@ + + + + Test for CSS Selectors + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_setPropertyWithNull.html b/layout/style/test/test_setPropertyWithNull.html new file mode 100644 index 0000000000..d54fd03cdd --- /dev/null +++ b/layout/style/test/test_setPropertyWithNull.html @@ -0,0 +1,47 @@ + + + + + + Test for Bug 830260 + + + + + +Mozilla Bug 830260 +

    + +
    +
    + + diff --git a/layout/style/test/test_shape_outside_CORS.html b/layout/style/test/test_shape_outside_CORS.html new file mode 100644 index 0000000000..2d2ee1c32f --- /dev/null +++ b/layout/style/test/test_shape_outside_CORS.html @@ -0,0 +1,59 @@ + + + + +CSS Test: shape-outside with a CORS violation + + + + + + + + + + +
    +
    allow (image is blank, so text is flush left)
    +
    +
    +
    refuse (image unread, so text is moved to box edge)
    +
    + + diff --git a/layout/style/test/test_shared_sheet_caching.html b/layout/style/test/test_shared_sheet_caching.html new file mode 100644 index 0000000000..e59c0c139e --- /dev/null +++ b/layout/style/test/test_shared_sheet_caching.html @@ -0,0 +1,35 @@ + + + + + + +Navigation + diff --git a/layout/style/test/test_sheet_privilege.html b/layout/style/test/test_sheet_privilege.html new file mode 100644 index 0000000000..d089fdc137 --- /dev/null +++ b/layout/style/test/test_sheet_privilege.html @@ -0,0 +1,33 @@ + +Test whether it can access a filed in MediaList with normal privilege after accessing with chrome privilege. + + + + diff --git a/layout/style/test/test_shorthand_property_getters.html b/layout/style/test/test_shorthand_property_getters.html new file mode 100644 index 0000000000..cade526183 --- /dev/null +++ b/layout/style/test/test_shorthand_property_getters.html @@ -0,0 +1,248 @@ + + + + + Test for Bug 376075 + + + + +Mozilla Bug 376075 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_specified_value_serialization.html b/layout/style/test/test_specified_value_serialization.html new file mode 100644 index 0000000000..5624b7398a --- /dev/null +++ b/layout/style/test/test_specified_value_serialization.html @@ -0,0 +1,281 @@ + + + + Test for miscellaneous specified value issues + + + + +Mozilla Bug +

    + + +
    +
    +
    + + diff --git a/layout/style/test/test_style_attr_listener.html b/layout/style/test/test_style_attr_listener.html new file mode 100644 index 0000000000..b824fe7ff4 --- /dev/null +++ b/layout/style/test/test_style_attr_listener.html @@ -0,0 +1,52 @@ + + + + Test for Bug 338679 (from bug 1340341) + + + + +
    + + + diff --git a/layout/style/test/test_style_attribute_quirks.html b/layout/style/test/test_style_attribute_quirks.html new file mode 100644 index 0000000000..5a5b87e122 --- /dev/null +++ b/layout/style/test/test_style_attribute_quirks.html @@ -0,0 +1,18 @@ + + + + + Test for Bug 915093 + + + + + +Mozilla Bug 915093 +
    +
    +
    + + diff --git a/layout/style/test/test_style_attribute_standards.html b/layout/style/test/test_style_attribute_standards.html new file mode 100644 index 0000000000..e6e64afc24 --- /dev/null +++ b/layout/style/test/test_style_attribute_standards.html @@ -0,0 +1,19 @@ + + + + + + Test for Bug 915093 + + + + + +Mozilla Bug 915093 +
    +
    +
    + + diff --git a/layout/style/test/test_style_struct_copy_constructors.html b/layout/style/test/test_style_struct_copy_constructors.html new file mode 100644 index 0000000000..95f727a58d --- /dev/null +++ b/layout/style/test/test_style_struct_copy_constructors.html @@ -0,0 +1,93 @@ + + + + + Test for style struct copy constructors + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_stylesheet_additions.html b/layout/style/test/test_stylesheet_additions.html new file mode 100644 index 0000000000..0e115e91f4 --- /dev/null +++ b/layout/style/test/test_stylesheet_additions.html @@ -0,0 +1,68 @@ + + + + Test for bug 1273303: Stylesheet additions and removals known to not + affect the document don't trigger restyles + + + +
    +
    +
    +
    +
    +
    + + + diff --git a/layout/style/test/test_stylesheet_clone_font_face.html b/layout/style/test/test_stylesheet_clone_font_face.html new file mode 100644 index 0000000000..e50bfec661 --- /dev/null +++ b/layout/style/test/test_stylesheet_clone_font_face.html @@ -0,0 +1,26 @@ + + + + + + + + +
    ABC
    + + diff --git a/layout/style/test/test_supports_rules.html b/layout/style/test/test_supports_rules.html new file mode 100644 index 0000000000..8b88fd7836 --- /dev/null +++ b/layout/style/test/test_supports_rules.html @@ -0,0 +1,88 @@ + + + + + Test for Bug 649740 + + + + + +Mozilla Bug 649740 +

    +

    +
    +
    +
    + + diff --git a/layout/style/test/test_system_font_serialization.html b/layout/style/test/test_system_font_serialization.html new file mode 100644 index 0000000000..10fb54c45a --- /dev/null +++ b/layout/style/test/test_system_font_serialization.html @@ -0,0 +1,84 @@ + + + + + Test for Bug 475214 + + + + +Mozilla Bug 475214 +

    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_text_decoration_shorthands.html b/layout/style/test/test_text_decoration_shorthands.html new file mode 100644 index 0000000000..d2cfed6667 --- /dev/null +++ b/layout/style/test/test_text_decoration_shorthands.html @@ -0,0 +1,136 @@ + + + + + Test parsing of text-decoration shorthands + + + + + + + + + diff --git a/layout/style/test/test_transitions.html b/layout/style/test/test_transitions.html new file mode 100644 index 0000000000..c26ba9be8f --- /dev/null +++ b/layout/style/test/test_transitions.html @@ -0,0 +1,787 @@ + + + + + Test for Bug 435441 + + + + + + +Mozilla Bug 435441 +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_and_reframes.html b/layout/style/test/test_transitions_and_reframes.html new file mode 100644 index 0000000000..dfc16e54d1 --- /dev/null +++ b/layout/style/test/test_transitions_and_reframes.html @@ -0,0 +1,298 @@ + + + + + + Test for Bug 625289 + + + + + +Mozilla Bug 625289 +
    +
    + This text has an i element in it. +
    +
    +
    + hello + this appears + hello +
    color transition
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_and_restyles.html b/layout/style/test/test_transitions_and_restyles.html new file mode 100644 index 0000000000..35fc608c20 --- /dev/null +++ b/layout/style/test/test_transitions_and_restyles.html @@ -0,0 +1,48 @@ + + + + + + Test for Bug 1030993 + + + + + +Mozilla Bug 1030993 +

    +
    +
    + + + diff --git a/layout/style/test/test_transitions_and_zoom.html b/layout/style/test/test_transitions_and_zoom.html new file mode 100644 index 0000000000..e95581be32 --- /dev/null +++ b/layout/style/test/test_transitions_and_zoom.html @@ -0,0 +1,49 @@ + + + + + Test for Bug 583219 + + + + + +Mozilla Bug 583219 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_at_start.html b/layout/style/test/test_transitions_at_start.html new file mode 100644 index 0000000000..bad36e7673 --- /dev/null +++ b/layout/style/test/test_transitions_at_start.html @@ -0,0 +1,39 @@ + + + + + + Test for transition value at start (Bug 1380133) + + + + + + +Test + + + diff --git a/layout/style/test/test_transitions_bug537151.html b/layout/style/test/test_transitions_bug537151.html new file mode 100644 index 0000000000..8d3b84a5fc --- /dev/null +++ b/layout/style/test/test_transitions_bug537151.html @@ -0,0 +1,51 @@ + + + + + Test for Bug 537151 + + + + + +Mozilla Bug 537151 +

    Paragraph

    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_cancel_near_end.html b/layout/style/test/test_transitions_cancel_near_end.html new file mode 100644 index 0000000000..496d95e6a1 --- /dev/null +++ b/layout/style/test/test_transitions_cancel_near_end.html @@ -0,0 +1,82 @@ + + + + + Test for Bug 613888 + + + + + + +Mozilla Bug 613888 +
    +  canceled on a half of the animation
    +  canceled too fast, and restyled on transitionend
    +  canceled too fast, but not restyled on transitionend
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_computed_value_combinations.html b/layout/style/test/test_transitions_computed_value_combinations.html new file mode 100644 index 0000000000..3dfad41e58 --- /dev/null +++ b/layout/style/test/test_transitions_computed_value_combinations.html @@ -0,0 +1,170 @@ + + + + + Test for Bug 435441 + + + + +Mozilla Bug 435441 + +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_computed_values.html b/layout/style/test/test_transitions_computed_values.html new file mode 100644 index 0000000000..7b350de6b2 --- /dev/null +++ b/layout/style/test/test_transitions_computed_values.html @@ -0,0 +1,113 @@ + + + + + Test for Bug 435441 + + + + +Mozilla Bug 435441 + +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_dynamic_changes.html b/layout/style/test/test_transitions_dynamic_changes.html new file mode 100644 index 0000000000..4d49db1e3a --- /dev/null +++ b/layout/style/test/test_transitions_dynamic_changes.html @@ -0,0 +1,106 @@ + + + + + Test for Bug 525530 + + + + +Mozilla Bug 525530 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_events.html b/layout/style/test/test_transitions_events.html new file mode 100644 index 0000000000..d04348bc0a --- /dev/null +++ b/layout/style/test/test_transitions_events.html @@ -0,0 +1,294 @@ + + + + + Test for Bug 531585 (transitionend event) + + + + + +Mozilla Bug 531585 +

    + + + + + + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html new file mode 100644 index 0000000000..058152adfb --- /dev/null +++ b/layout/style/test/test_transitions_per_property.html @@ -0,0 +1,3245 @@ + + + + + Test for Bug 435441 + + + + + + + + +Mozilla Bug 435441 + + +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_replacement_on_busy_frame.html b/layout/style/test/test_transitions_replacement_on_busy_frame.html new file mode 100644 index 0000000000..527c98ae85 --- /dev/null +++ b/layout/style/test/test_transitions_replacement_on_busy_frame.html @@ -0,0 +1,100 @@ + + + + + + Test for bug 1167519 + + + + + + + +
    + + + diff --git a/layout/style/test/test_transitions_replacement_with_setKeyframes.html b/layout/style/test/test_transitions_replacement_with_setKeyframes.html new file mode 100644 index 0000000000..85e9e40127 --- /dev/null +++ b/layout/style/test/test_transitions_replacement_with_setKeyframes.html @@ -0,0 +1,88 @@ + + + + + + Test for bug 1292001 + + + + + + + +
    + + + diff --git a/layout/style/test/test_transitions_step_functions.html b/layout/style/test/test_transitions_step_functions.html new file mode 100644 index 0000000000..c0205a8d2f --- /dev/null +++ b/layout/style/test/test_transitions_step_functions.html @@ -0,0 +1,131 @@ + + + + + Test for Bug 435441 + + + + + + +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_unclosed_parentheses.html b/layout/style/test/test_unclosed_parentheses.html new file mode 100644 index 0000000000..7e8052892c --- /dev/null +++ b/layout/style/test/test_unclosed_parentheses.html @@ -0,0 +1,262 @@ + + + + + Test for Bug 575672 + + + + + + +Mozilla Bug 575672 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_unicode_range_loading.html b/layout/style/test/test_unicode_range_loading.html new file mode 100644 index 0000000000..43622e2ae5 --- /dev/null +++ b/layout/style/test/test_unicode_range_loading.html @@ -0,0 +1,366 @@ + + + + + unicode-range load tests using font loading api + + + + + + + + + +
    +
    
    +
    +
    +
    + + + + diff --git a/layout/style/test/test_units_angle.html b/layout/style/test/test_units_angle.html new file mode 100644 index 0000000000..a4432b7650 --- /dev/null +++ b/layout/style/test/test_units_angle.html @@ -0,0 +1,52 @@ + + + + Test for serialization and equivalence of angle units + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_units_frequency.html b/layout/style/test/test_units_frequency.html new file mode 100644 index 0000000000..cb5c0de20d --- /dev/null +++ b/layout/style/test/test_units_frequency.html @@ -0,0 +1,56 @@ + + + + Test for serialization and equivalence of frequency units + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_units_length.html b/layout/style/test/test_units_length.html new file mode 100644 index 0000000000..623f13df33 --- /dev/null +++ b/layout/style/test/test_units_length.html @@ -0,0 +1,60 @@ + + + + Test for serialization and equivalence of length units + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_units_time.html b/layout/style/test/test_units_time.html new file mode 100644 index 0000000000..16211c0207 --- /dev/null +++ b/layout/style/test/test_units_time.html @@ -0,0 +1,47 @@ + + + + Test for serialization and equivalence of time units + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_use_counters.html b/layout/style/test/test_use_counters.html new file mode 100644 index 0000000000..0706c9702d --- /dev/null +++ b/layout/style/test/test_use_counters.html @@ -0,0 +1,159 @@ + +Test for Bug 1425700: CSS properties use-counters + + + + + + + diff --git a/layout/style/test/test_user_sheet_shadow_dom.html b/layout/style/test/test_user_sheet_shadow_dom.html new file mode 100644 index 0000000000..cd7a44b308 --- /dev/null +++ b/layout/style/test/test_user_sheet_shadow_dom.html @@ -0,0 +1,48 @@ + +Test for bug 1576229 - Nodes in Shadow DOM react properly to dynamic changes in user sheets + + +
    + + diff --git a/layout/style/test/test_value_cloning.html b/layout/style/test/test_value_cloning.html new file mode 100644 index 0000000000..ceaa9d3c66 --- /dev/null +++ b/layout/style/test/test_value_cloning.html @@ -0,0 +1,181 @@ + + + + + Test for cloning of CSS property values (including 'inherit', 'initial' and 'unset') + + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_value_computation.html b/layout/style/test/test_value_computation.html new file mode 100644 index 0000000000..df38a24b9b --- /dev/null +++ b/layout/style/test/test_value_computation.html @@ -0,0 +1,236 @@ + + + + + Test for computation of values in property database + + + + + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_value_storage.html b/layout/style/test/test_value_storage.html new file mode 100644 index 0000000000..542cad91ed --- /dev/null +++ b/layout/style/test/test_value_storage.html @@ -0,0 +1,365 @@ + + + + + Test for parsing, storage, and serialization of CSS values + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_variable_serialization_computed.html b/layout/style/test/test_variable_serialization_computed.html new file mode 100644 index 0000000000..2814e4ab93 --- /dev/null +++ b/layout/style/test/test_variable_serialization_computed.html @@ -0,0 +1,79 @@ + +Test serialization of computed CSS variable values + + + + +
    + +
    + + diff --git a/layout/style/test/test_variable_serialization_specified.html b/layout/style/test/test_variable_serialization_specified.html new file mode 100644 index 0000000000..cae8871cb2 --- /dev/null +++ b/layout/style/test/test_variable_serialization_specified.html @@ -0,0 +1,116 @@ + +Test serialization of specified CSS variable values + + + + + + + + diff --git a/layout/style/test/test_variables.html b/layout/style/test/test_variables.html new file mode 100644 index 0000000000..e26dc5a0f7 --- /dev/null +++ b/layout/style/test/test_variables.html @@ -0,0 +1,129 @@ + +Assorted CSS variable tests + + + + + + + + + + + + +
    + + + +
    + + + + + + + + diff --git a/layout/style/test/test_variables_loop.html b/layout/style/test/test_variables_loop.html new file mode 100644 index 0000000000..76e97e26f3 --- /dev/null +++ b/layout/style/test/test_variables_loop.html @@ -0,0 +1,31 @@ + +CSS variables loop resolving + + + + +
    +
    +
    + diff --git a/layout/style/test/test_variables_order.html b/layout/style/test/test_variables_order.html new file mode 100644 index 0000000000..5adb9edf75 --- /dev/null +++ b/layout/style/test/test_variables_order.html @@ -0,0 +1,53 @@ + +CSS variables order tests + + + + + + +
    + + diff --git a/layout/style/test/test_video_object_fit.html b/layout/style/test/test_video_object_fit.html new file mode 100644 index 0000000000..e673ba204e --- /dev/null +++ b/layout/style/test/test_video_object_fit.html @@ -0,0 +1,53 @@ + + + + + + Test for Bug 1065766 + + + + +Mozilla Bug 1065766 + +
    +
    +
    + + diff --git a/layout/style/test/test_viewport_scrollbar_causing_reflow.html b/layout/style/test/test_viewport_scrollbar_causing_reflow.html new file mode 100644 index 0000000000..ced14f352a --- /dev/null +++ b/layout/style/test/test_viewport_scrollbar_causing_reflow.html @@ -0,0 +1,130 @@ + + + + + + Test for Bug 1367568 + + + + +Mozilla Bug 1367568 +
    + +
    fixed-width
    (child)
    +
    fixed-width
    (child)
    +
    fixed-width
    (child)
    +
    fixed-width
    (child)
    +
    fixed-width
    (child)
    +
    fixed-width
    (child)
    +
    + abs-fixed-width +
    (child)
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_viewport_units.html b/layout/style/test/test_viewport_units.html new file mode 100644 index 0000000000..fa7df88eb6 --- /dev/null +++ b/layout/style/test/test_viewport_units.html @@ -0,0 +1,66 @@ + + + + + Test for dynamic changes to CSS 'vh', 'vw', 'vmin', and 'vmax' units + + + + +Mozilla Bug 804970 + +
    +
    +
    + + diff --git a/layout/style/test/test_visited_image_loading.html b/layout/style/test/test_visited_image_loading.html new file mode 100644 index 0000000000..09aae8e53c --- /dev/null +++ b/layout/style/test/test_visited_image_loading.html @@ -0,0 +1,68 @@ + + + + + Test for Bug 557287 + + + + +Mozilla Bug 147777 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_visited_image_loading_empty.html b/layout/style/test/test_visited_image_loading_empty.html new file mode 100644 index 0000000000..6687254720 --- /dev/null +++ b/layout/style/test/test_visited_image_loading_empty.html @@ -0,0 +1,68 @@ + + + + + Test for Bug 557287 + + + + +Mozilla Bug 147777 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_visited_lying.html b/layout/style/test/test_visited_lying.html new file mode 100644 index 0000000000..116d301cf1 --- /dev/null +++ b/layout/style/test/test_visited_lying.html @@ -0,0 +1,97 @@ + + + + + Test for Bug 147777 + + + + + +Mozilla Bug 147777 + +
    +
    +
    + + diff --git a/layout/style/test/test_visited_pref.html b/layout/style/test/test_visited_pref.html new file mode 100644 index 0000000000..481a71bfef --- /dev/null +++ b/layout/style/test/test_visited_pref.html @@ -0,0 +1,112 @@ + + + + + Test for visited link coloring pref Bug 147777 + + + + + + +Mozilla Bug 147777 + +
    +
    +
    + + diff --git a/layout/style/test/test_visited_reftests.html b/layout/style/test/test_visited_reftests.html new file mode 100644 index 0000000000..0353d948fc --- /dev/null +++ b/layout/style/test/test_visited_reftests.html @@ -0,0 +1,210 @@ + + + + + Test for Bug 147777 + + + + + +Mozilla Bug 147777 +
    +
    +
    + + diff --git a/layout/style/test/test_webkit_device_pixel_ratio.html b/layout/style/test/test_webkit_device_pixel_ratio.html new file mode 100644 index 0000000000..69e50c58ff --- /dev/null +++ b/layout/style/test/test_webkit_device_pixel_ratio.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 1176968 + + + + + + +Mozilla Bug 1176968 + + +
    +
    +
    +
    + +
    + + diff --git a/layout/style/test/test_webkit_flex_display.html b/layout/style/test/test_webkit_flex_display.html new file mode 100644 index 0000000000..2f329ed67d --- /dev/null +++ b/layout/style/test/test_webkit_flex_display.html @@ -0,0 +1,46 @@ + + + + + Test for Bug 1274096 + + + + +Mozilla Bug 1274096 + + +
    +
    +
    + + diff --git a/layout/style/test/unstyled-frame.css b/layout/style/test/unstyled-frame.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/layout/style/test/unstyled-frame.xml b/layout/style/test/unstyled-frame.xml new file mode 100644 index 0000000000..833b4f112f --- /dev/null +++ b/layout/style/test/unstyled-frame.xml @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/test/unstyled.css b/layout/style/test/unstyled.css new file mode 100644 index 0000000000..82767f9b2f --- /dev/null +++ b/layout/style/test/unstyled.css @@ -0,0 +1,2 @@ +/* we're testing computed style on elements without frames */ +root { display: none } diff --git a/layout/style/test/unstyled.xml b/layout/style/test/unstyled.xml new file mode 100644 index 0000000000..86b7c54acd --- /dev/null +++ b/layout/style/test/unstyled.xml @@ -0,0 +1,3 @@ + + + diff --git a/layout/style/test/viewport_units_iframe.html b/layout/style/test/viewport_units_iframe.html new file mode 100644 index 0000000000..fd71a3cd3e --- /dev/null +++ b/layout/style/test/viewport_units_iframe.html @@ -0,0 +1,6 @@ + +viewport units test +
    +
    +
    +
    diff --git a/layout/style/test/visited-lying-inner.html b/layout/style/test/visited-lying-inner.html new file mode 100644 index 0000000000..ad1dac7587 --- /dev/null +++ b/layout/style/test/visited-lying-inner.html @@ -0,0 +1,8 @@ + +Test document for test_visited_lying.html + + + diff --git a/layout/style/test/visited-pref-iframe.html b/layout/style/test/visited-pref-iframe.html new file mode 100644 index 0000000000..31da176e44 --- /dev/null +++ b/layout/style/test/visited-pref-iframe.html @@ -0,0 +1,7 @@ + +iframe for test_visited_pref.html + +link diff --git a/layout/style/test/visited_image_loading.sjs b/layout/style/test/visited_image_loading.sjs new file mode 100644 index 0000000000..79c03d7b54 --- /dev/null +++ b/layout/style/test/visited_image_loading.sjs @@ -0,0 +1,83 @@ +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + var query = request.queryString; + switch (query) { + case "reset": + response.setHeader("Content-Type", "application/ecmascript", false); + setState("1l", ""); + setState("1v", ""); + setState("2l", ""); + setState("2v", ""); + break; + case "1l": + case "1v": + case "2l": + case "2v": + setState(query, getState(query) + "load"); + response.setStatusLine("1.1", 302, "Found"); + // redirect to a solid blue image + response.setHeader( + "Location", + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12NgYPgPAAEDAQDZqt2zAAAAAElFTkSuQmCC" + ); + response.setHeader("Content-Type", "text/plain", false); + break; + + case "waitforresult": + response.setHeader("Content-Type", "application/ecmascript", false); + response.write("var start = Date.now();\n"); + // fall through! + + case "waitforresult-internal": + response.setHeader("Content-Type", "application/ecmascript", false); + response.write( + "if ('" + + getState("1l") + + "' == 'load' && '" + + getState("1v") + + "' == '' && '" + + getState("2l") + + "' == 'load' && '" + + getState("2v") + + "' == '') { \n" + ); + response.write("setTimeout(function() {\n"); + response.write("var s = document.createElement('script');\n"); + response.write("s.src = 'visited_image_loading.sjs?result';\n"); + response.write("document.body.appendChild(s);"); + response.write("}, Math.max(100, 2 * (Date.now() - start)));\n"); + response.write("} else setTimeout(function() {\n"); + response.write("var s = document.createElement('script');\n"); + response.write( + "s.src = 'visited_image_loading.sjs?waitforresult-internal';\n" + ); + response.write("document.body.appendChild(s);"); + response.write("}, 10);\n"); + break; + + case "result": + response.setHeader("Content-Type", "application/ecmascript", false); + response.write( + "is('" + + getState("1l") + + "', 'load', 'image 1l should have been loaded once')\n" + ); + response.write( + "is('" + + getState("1v") + + "', '', 'image 1v should not have been loaded')\n" + ); + response.write( + "is('" + + getState("2l") + + "', 'load', 'image 2l should have been loaded once')\n" + ); + response.write( + "is('" + + getState("2v") + + "', '', 'image 2v should not have been loaded')\n" + ); + response.write("SimpleTest.finish()"); + break; + } +} diff --git a/layout/style/test/visited_image_loading_frame.html b/layout/style/test/visited_image_loading_frame.html new file mode 100644 index 0000000000..f919f5eb75 --- /dev/null +++ b/layout/style/test/visited_image_loading_frame.html @@ -0,0 +1,15 @@ + +Test for :visited image loading + +unvisited link +visited link diff --git a/layout/style/test/visited_image_loading_frame_empty.html b/layout/style/test/visited_image_loading_frame_empty.html new file mode 100644 index 0000000000..21579bb9ca --- /dev/null +++ b/layout/style/test/visited_image_loading_frame_empty.html @@ -0,0 +1,15 @@ + +Test for :visited image loading + + + diff --git a/layout/style/tools/cleanup_computed_getters.py b/layout/style/tools/cleanup_computed_getters.py new file mode 100644 index 0000000000..301986751e --- /dev/null +++ b/layout/style/tools/cleanup_computed_getters.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# 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/. + + +""" +Script to remove unused getters in nsComputedDOMStyle. + +It needs to be run from the topsrcdir, and it requires passing in the objdir +as first argument. It can only be run after nsComputedDOMStyleGenerated.inc +is generated in the objdir. +""" + +import re +import sys + +from pathlib import Path + +if len(sys.argv) != 2: + print("Usage: {} objdir".format(sys.argv[0])) + exit(1) + +generated = Path(sys.argv[1]) / "layout" / "style" +generated = generated / "nsComputedDOMStyleGenerated.inc" +RE_GENERATED = re.compile(r"DoGet\w+") +keeping = set() +with generated.open() as f: + for line in f: + m = RE_GENERATED.search(line) + if m is not None: + keeping.add(m.group(0)) + +HEADER = "layout/style/nsComputedDOMStyle.h" +SOURCE = "layout/style/nsComputedDOMStyle.cpp" + +# We need to keep functions invoked by others +RE_DEF = re.compile(r"nsComputedDOMStyle::(DoGet\w+)\(\)") +RE_SRC = re.compile(r"\b(DoGet\w+)\(\)") +with open(SOURCE, "r") as f: + for line in f: + m = RE_DEF.search(line) + if m is not None: + continue + m = RE_SRC.search(line) + if m is not None: + keeping.add(m.group(1)) + +removing = set() +remaining_lines = [] +with open(HEADER, "r") as f: + for line in f: + m = RE_SRC.search(line) + if m is not None: + name = m.group(1) + if name not in keeping: + print("Removing " + name) + removing.add(name) + continue + remaining_lines.append(line) + +with open(HEADER, "w", newline="") as f: + f.writelines(remaining_lines) + +remaining_lines = [] +is_removing = False +with open(SOURCE, "r") as f: + for line in f: + if is_removing: + if line == "}\n": + is_removing = False + continue + m = RE_DEF.search(line) + if m is not None: + name = m.group(1) + if name in removing: + remaining_lines.pop() + if remaining_lines[-1] == "\n": + remaining_lines.pop() + is_removing = True + continue + remaining_lines.append(line) + +with open(SOURCE, "w", newline="") as f: + f.writelines(remaining_lines) -- cgit v1.2.3