From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- layout/style/AnimationCollection.cpp | 172 + layout/style/AnimationCollection.h | 147 + layout/style/AnimationCommon.cpp | 54 + layout/style/AnimationCommon.h | 256 + layout/style/CSS.cpp | 107 + layout/style/CSS.h | 44 + layout/style/CSSCalc.h | 361 + layout/style/CSSEnabledState.h | 39 + layout/style/CSSLexer.cpp | 166 + layout/style/CSSLexer.h | 39 + layout/style/CSSRuleList.cpp | 34 + layout/style/CSSRuleList.h | 66 + layout/style/CSSStyleSheet.cpp | 2026 ++ layout/style/CSSStyleSheet.h | 281 + layout/style/CSSUnprefixingService.js | 342 + layout/style/CSSUnprefixingService.manifest | 2 + layout/style/CSSValue.h | 47 + layout/style/CSSVariableDeclarations.cpp | 195 + layout/style/CSSVariableDeclarations.h | 139 + layout/style/CSSVariableImageTable.h | 190 + layout/style/CSSVariableResolver.cpp | 266 + layout/style/CSSVariableResolver.h | 148 + layout/style/CSSVariableValues.cpp | 147 + layout/style/CSSVariableValues.h | 147 + layout/style/CounterStyleManager.cpp | 2135 +++ layout/style/CounterStyleManager.h | 192 + layout/style/Declaration.cpp | 1988 ++ layout/style/Declaration.h | 417 + layout/style/DeclarationBlock.h | 146 + layout/style/DeclarationBlockInlines.h | 114 + layout/style/ErrorReporter.cpp | 382 + layout/style/ErrorReporter.h | 113 + layout/style/FontFace.cpp | 808 + layout/style/FontFace.h | 271 + layout/style/FontFaceSet.cpp | 1848 ++ layout/style/FontFaceSet.h | 356 + layout/style/FontFaceSetIterator.cpp | 80 + layout/style/FontFaceSetIterator.h | 44 + layout/style/GenerateCSSPropsGenerated.py | 104 + layout/style/GroupRule.h | 101 + layout/style/HandleRefPtr.h | 133 + layout/style/ImageDocument.css | 31 + layout/style/ImageLoader.cpp | 529 + layout/style/ImageLoader.h | 124 + layout/style/ImportRule.h | 72 + layout/style/IncrementalClearCOMRuleArray.cpp | 81 + layout/style/IncrementalClearCOMRuleArray.h | 28 + layout/style/LayerAnimationInfo.cpp | 53 + layout/style/LayerAnimationInfo.h | 33 + layout/style/Loader.cpp | 2694 +++ layout/style/Loader.h | 598 + layout/style/Makefile.in | 16 + layout/style/MediaQueryList.cpp | 207 + layout/style/MediaQueryList.h | 93 + layout/style/NameSpaceRule.h | 70 + layout/style/PythonCSSProps.h | 41 + layout/style/Rule.h | 141 + layout/style/RuleNodeCacheConditions.cpp | 53 + layout/style/RuleNodeCacheConditions.h | 156 + layout/style/RuleProcessorCache.cpp | 286 + layout/style/RuleProcessorCache.h | 150 + layout/style/SVGAttrAnimationRuleProcessor.cpp | 122 + layout/style/SVGAttrAnimationRuleProcessor.h | 68 + layout/style/ServoBindingList.h | 146 + layout/style/ServoBindingTypes.h | 144 + layout/style/ServoBindings.cpp | 1083 ++ layout/style/ServoBindings.h | 285 + layout/style/ServoDeclarationBlock.cpp | 110 + layout/style/ServoDeclarationBlock.h | 72 + layout/style/ServoElementSnapshot.cpp | 46 + layout/style/ServoElementSnapshot.h | 169 + layout/style/ServoStyleSet.cpp | 479 + layout/style/ServoStyleSet.h | 175 + layout/style/ServoStyleSheet.cpp | 143 + layout/style/ServoStyleSheet.h | 90 + layout/style/ServoTypes.h | 40 + layout/style/ServoUtils.h | 82 + layout/style/SheetParsingMode.h | 43 + layout/style/SheetType.h | 38 + layout/style/StyleAnimationValue.cpp | 4990 +++++ layout/style/StyleAnimationValue.h | 602 + layout/style/StyleBackendType.h | 23 + layout/style/StyleComplexColor.h | 45 + layout/style/StyleContextSource.h | 161 + layout/style/StyleRule.cpp | 1577 ++ layout/style/StyleRule.h | 369 + layout/style/StyleSetHandle.h | 219 + layout/style/StyleSetHandleInlines.h | 267 + layout/style/StyleSheet.cpp | 329 + layout/style/StyleSheet.h | 210 + layout/style/StyleSheetInfo.h | 52 + layout/style/StyleSheetInlines.h | 176 + layout/style/StyleStructContext.h | 121 + layout/style/TopLevelImageDocument.css | 40 + layout/style/TopLevelVideoDocument.css | 32 + layout/style/contenteditable.css | 343 + layout/style/crashtests/1017798-1.css | 84 + layout/style/crashtests/1017798-1.html | 124 + 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/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/1227501-1.html | 8 + layout/style/crashtests/1230408-1.html | 8 + layout/style/crashtests/1233135-1.html | 13 + layout/style/crashtests/1233135-2.html | 11 + layout/style/crashtests/1238660-1.html | 19 + layout/style/crashtests/1245260-1.html | 53 + layout/style/crashtests/1247865-1.html | 19 + layout/style/crashtests/1264396-1.html | 14 + layout/style/crashtests/1264949.html | 23 + layout/style/crashtests/1265611-1.html | 16 + 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/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/1321357-1.html | 12 + layout/style/crashtests/1356601-1.html | 18 + layout/style/crashtests/147777-1.html | 6 + 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 | 36 + 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.xul | 6 + layout/style/crashtests/418007-1.xhtml | 24 + layout/style/crashtests/431705-1.xul | 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/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/621596-1.html | 18 + layout/style/crashtests/622314-1.xhtml | 26 + layout/style/crashtests/637242.xhtml | 27 + layout/style/crashtests/645142.html | 11 + layout/style/crashtests/645951-1-ref.html | 4 + layout/style/crashtests/645951-1.css | 1 + layout/style/crashtests/645951-1.html | 11 + layout/style/crashtests/652976-1.svg | 10 + 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/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/812824.html | 1 + layout/style/crashtests/822766-1.html | 31 + layout/style/crashtests/822842.html | 13 + layout/style/crashtests/827591-1.html | 19 + layout/style/crashtests/829817.html | 20 + layout/style/crashtests/840898.html | 17 + 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/880862.html | 28 + 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 + layout/style/crashtests/crashtests.list | 166 + .../style/crashtests/font-face-truncated-src.html | 2 + .../style/crashtests/large_border_image_width.html | 9 + .../crashtests/long-url-list-stack-overflow.html | 23 + layout/style/designmode.css | 8 + layout/style/generate-stylestructlist.py | 165 + layout/style/jar.mn | 35 + layout/style/moz.build | 264 + layout/style/nsAnimationManager.cpp | 1082 ++ layout/style/nsAnimationManager.h | 361 + layout/style/nsCSSAnonBoxList.h | 103 + layout/style/nsCSSAnonBoxes.cpp | 52 + layout/style/nsCSSAnonBoxes.h | 34 + layout/style/nsCSSCounterDescList.h | 15 + layout/style/nsCSSDataBlock.cpp | 802 + layout/style/nsCSSDataBlock.h | 372 + layout/style/nsCSSFontDescList.h | 14 + layout/style/nsCSSKeywordList.h | 799 + layout/style/nsCSSKeywords.cpp | 87 + layout/style/nsCSSKeywords.h | 42 + layout/style/nsCSSParser.cpp | 18324 +++++++++++++++++++ layout/style/nsCSSParser.h | 346 + layout/style/nsCSSPropAliasList.h | 498 + layout/style/nsCSSPropList.h | 4635 +++++ layout/style/nsCSSPropLogicalGroupList.h | 56 + layout/style/nsCSSPropertyID.h | 118 + layout/style/nsCSSPropertyIDSet.h | 107 + layout/style/nsCSSProps.cpp | 3428 ++++ layout/style/nsCSSProps.h | 895 + layout/style/nsCSSPropsGenerated.inc.in | 17 + layout/style/nsCSSPseudoClassList.h | 253 + layout/style/nsCSSPseudoClasses.cpp | 131 + layout/style/nsCSSPseudoClasses.h | 90 + layout/style/nsCSSPseudoElementList.h | 87 + layout/style/nsCSSPseudoElements.cpp | 120 + layout/style/nsCSSPseudoElements.h | 128 + layout/style/nsCSSRuleProcessor.cpp | 4061 ++++ layout/style/nsCSSRuleProcessor.h | 292 + layout/style/nsCSSRules.cpp | 3312 ++++ layout/style/nsCSSRules.h | 673 + layout/style/nsCSSScanner.cpp | 1380 ++ layout/style/nsCSSScanner.h | 397 + layout/style/nsCSSValue.cpp | 3237 ++++ layout/style/nsCSSValue.h | 1943 ++ layout/style/nsComputedDOMStyle.cpp | 6698 +++++++ layout/style/nsComputedDOMStyle.h | 749 + layout/style/nsComputedDOMStylePropertyList.h | 361 + layout/style/nsDOMCSSAttrDeclaration.cpp | 204 + layout/style/nsDOMCSSAttrDeclaration.h | 57 + layout/style/nsDOMCSSDeclaration.cpp | 406 + layout/style/nsDOMCSSDeclaration.h | 174 + layout/style/nsDOMCSSRGBColor.cpp | 38 + layout/style/nsDOMCSSRGBColor.h | 67 + layout/style/nsDOMCSSRect.cpp | 77 + layout/style/nsDOMCSSRect.h | 52 + layout/style/nsDOMCSSValueList.cpp | 129 + layout/style/nsDOMCSSValueList.h | 73 + layout/style/nsFontFaceLoader.cpp | 306 + layout/style/nsFontFaceLoader.h | 63 + layout/style/nsFontFaceUtils.cpp | 151 + layout/style/nsFontFaceUtils.h | 23 + layout/style/nsHTMLCSSStyleSheet.cpp | 217 + layout/style/nsHTMLCSSStyleSheet.h | 80 + layout/style/nsHTMLStyleSheet.cpp | 592 + layout/style/nsHTMLStyleSheet.h | 181 + layout/style/nsICSSDeclaration.h | 174 + layout/style/nsICSSLoaderObserver.h | 44 + layout/style/nsICSSPseudoComparator.h | 19 + layout/style/nsICSSStyleRuleDOMWrapper.h | 32 + layout/style/nsICSSUnprefixingService.idl | 76 + layout/style/nsIMediaList.h | 318 + layout/style/nsIStyleRule.h | 96 + layout/style/nsIStyleRuleProcessor.h | 140 + layout/style/nsLayoutStylesheetCache.cpp | 1000 + layout/style/nsLayoutStylesheetCache.h | 135 + layout/style/nsMediaFeatures.cpp | 813 + layout/style/nsMediaFeatures.h | 91 + layout/style/nsNthIndexCache.cpp | 155 + layout/style/nsNthIndexCache.h | 110 + layout/style/nsROCSSPrimitiveValue.cpp | 722 + layout/style/nsROCSSPrimitiveValue.h | 135 + layout/style/nsRuleData.cpp | 55 + layout/style/nsRuleData.h | 128 + layout/style/nsRuleNode.cpp | 10736 +++++++++++ layout/style/nsRuleNode.h | 1090 ++ layout/style/nsRuleProcessorData.h | 595 + layout/style/nsRuleWalker.h | 108 + layout/style/nsStyleConsts.h | 1289 ++ layout/style/nsStyleContext.cpp | 1836 ++ layout/style/nsStyleContext.h | 853 + layout/style/nsStyleCoord.cpp | 421 + layout/style/nsStyleCoord.h | 649 + layout/style/nsStyleSet.cpp | 2537 +++ layout/style/nsStyleSet.h | 600 + layout/style/nsStyleStruct.cpp | 4261 +++++ layout/style/nsStyleStruct.h | 4002 ++++ layout/style/nsStyleStructFwd.h | 64 + layout/style/nsStyleStructInlines.h | 264 + layout/style/nsStyleTransformMatrix.cpp | 1061 ++ layout/style/nsStyleTransformMatrix.h | 226 + layout/style/nsStyleUtil.cpp | 783 + layout/style/nsStyleUtil.h | 207 + layout/style/nsTransitionManager.cpp | 1047 ++ layout/style/nsTransitionManager.h | 447 + layout/style/res/accessiblecaret-normal@1.5x.png | Bin 0 -> 16477 bytes layout/style/res/accessiblecaret-normal@1x.png | Bin 0 -> 15965 bytes layout/style/res/accessiblecaret-normal@2.25x.png | Bin 0 -> 17193 bytes layout/style/res/accessiblecaret-normal@2x.png | Bin 0 -> 17190 bytes .../style/res/accessiblecaret-tilt-left@1.5x.png | Bin 0 -> 16342 bytes layout/style/res/accessiblecaret-tilt-left@1x.png | Bin 0 -> 15894 bytes .../style/res/accessiblecaret-tilt-left@2.25x.png | Bin 0 -> 17063 bytes layout/style/res/accessiblecaret-tilt-left@2x.png | Bin 0 -> 16756 bytes .../style/res/accessiblecaret-tilt-right@1.5x.png | Bin 0 -> 16360 bytes layout/style/res/accessiblecaret-tilt-right@1x.png | Bin 0 -> 15886 bytes .../style/res/accessiblecaret-tilt-right@2.25x.png | Bin 0 -> 17087 bytes layout/style/res/accessiblecaret-tilt-right@2x.png | Bin 0 -> 16763 bytes layout/style/res/arrow-left.gif | Bin 0 -> 57 bytes layout/style/res/arrow-right.gif | Bin 0 -> 57 bytes layout/style/res/arrow.gif | Bin 0 -> 56 bytes layout/style/res/arrowd-left.gif | Bin 0 -> 60 bytes layout/style/res/arrowd-right.gif | Bin 0 -> 59 bytes layout/style/res/arrowd.gif | Bin 0 -> 59 bytes layout/style/res/counterstyles.css | 365 + layout/style/res/forms.css | 1137 ++ layout/style/res/html.css | 863 + layout/style/res/noframes.css | 13 + layout/style/res/noscript.css | 9 + layout/style/res/number-control.css | 18 + layout/style/res/plaintext.css | 9 + layout/style/res/quirk.css | 203 + layout/style/res/ua.css | 473 + layout/style/res/viewsource.css | 101 + layout/style/test/BitPattern.woff | Bin 0 -> 6248 bytes layout/style/test/ListCSSProperties.cpp | 193 + layout/style/test/ParseCSS.cpp | 89 + layout/style/test/TestCSSPropertyLookup.cpp | 172 + layout/style/test/additional_sheets_helper.html | 7 + layout/style/test/animation_utils.js | 707 + layout/style/test/browser.ini | 8 + layout/style/test/browser_bug453896.js | 13 + .../test/browser_newtab_share_rule_processors.js | 38 + layout/style/test/bug453896_iframe.html | 66 + layout/style/test/bug517224.sjs | 24 + layout/style/test/bug732209-css.sjs | 19 + layout/style/test/ccd-quirks.html | 124 + layout/style/test/ccd-standards.html | 123 + layout/style/test/ccd.sjs | 73 + layout/style/test/chrome/bug418986-2.js | 314 + layout/style/test/chrome/bug535806-css.css | 1 + layout/style/test/chrome/bug535806-html.html | 8 + layout/style/test/chrome/bug535806-xul.xul | 8 + layout/style/test/chrome/chrome.ini | 21 + layout/style/test/chrome/hover_empty.html | 4 + layout/style/test/chrome/hover_helper.html | 270 + 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 + .../test/chrome/test_author_specified_style.html | 55 + layout/style/test/chrome/test_bug1157097.html | 31 + layout/style/test/chrome/test_bug1160724.xul | 76 + layout/style/test/chrome/test_bug418986-2.xul | 30 + layout/style/test/chrome/test_bug535806.xul | 43 + layout/style/test/chrome/test_display_mode.html | 94 + .../test/chrome/test_display_mode_reflow.html | 74 + layout/style/test/chrome/test_hover.html | 29 + .../style/test/chrome/test_moz_document_rules.html | 97 + layout/style/test/css_properties_like_longhand.js | 3 + layout/style/test/descriptor_database.js | 72 + layout/style/test/display_mode_reflow_iframe.html | 23 + layout/style/test/empty.html | 1 + layout/style/test/file_animations_async_tests.html | 77 + .../file_animations_effect_timing_duration.html | 86 + .../file_animations_effect_timing_enddelay.html | 146 + .../file_animations_effect_timing_iterations.html | 73 + .../style/test/file_animations_iterationstart.html | 60 + layout/style/test/file_animations_pausing.html | 85 + .../style/test/file_animations_playbackrate.html | 102 + .../test/file_animations_styles_on_event.html | 70 + .../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_bug645998-1.css | 1 + layout/style/test/file_bug645998-2.css | 1 + layout/style/test/file_bug829816.css | Bin 0 -> 76 bytes .../style/test/file_font_loading_api_vframe.html | 2 + ...file_transitions_replacement_on_busy_frame.html | 93 + .../file_transitions_with_disabled_properties.html | 46 + layout/style/test/flexbox_layout_testcases.js | 1398 ++ layout/style/test/gen-css-properties.py | 24 + .../test/media_queries_dynamic_xbl_binding.xml | 13 + .../test/media_queries_dynamic_xbl_iframe.html | 5 + .../style/test/media_queries_dynamic_xbl_style.css | 6 + layout/style/test/media_queries_iframe.html | 15 + layout/style/test/mochitest.ini | 312 + layout/style/test/moz.build | 126 + layout/style/test/neverending_font_load.sjs | 6 + layout/style/test/neverending_stylesheet_load.sjs | 6 + .../style/test/newtab_share_rule_processors.html | 22 + 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 | 7917 ++++++++ layout/style/test/redirect.sjs | 5 + layout/style/test/redundant_font_download.sjs | 60 + layout/style/test/style_attribute_tests.js | 27 + .../style/test/support/external-variable-url.css | 3 + layout/style/test/test_acid3_test46.html | 141 + layout/style/test/test_addSheet.html | 46 + layout/style/test/test_additional_sheets.html | 314 + .../test/test_align_justify_computed_values.html | 529 + .../test/test_align_shorthand_serialization.html | 123 + layout/style/test/test_all_shorthand.html | 159 + layout/style/test/test_animations.html | 2047 +++ layout/style/test/test_animations_async_tests.html | 26 + .../test/test_animations_dynamic_changes.html | 65 + .../test_animations_effect_timing_duration.html | 24 + .../test_animations_effect_timing_enddelay.html | 24 + .../test_animations_effect_timing_iterations.html | 24 + .../test_animations_event_handler_attribute.html | 147 + layout/style/test/test_animations_event_order.html | 585 + .../style/test/test_animations_iterationstart.html | 28 + layout/style/test/test_animations_omta.html | 2392 +++ layout/style/test/test_animations_omta_start.html | 189 + layout/style/test/test_animations_pausing.html | 28 + .../style/test/test_animations_playbackrate.html | 28 + .../test/test_animations_styles_on_event.html | 28 + .../test_animations_with_disabled_properties.html | 34 + layout/style/test/test_any_dynamic.html | 49 + layout/style/test/test_asyncopen2.html | 54 + .../style/test/test_at_rule_parse_serialize.html | 43 + .../test/test_attribute_selector_eof_behavior.html | 18 + layout/style/test/test_background_blend_mode.html | 58 + layout/style/test/test_box_size_keywords.html | 172 + layout/style/test/test_bug1055933.html | 42 + layout/style/test/test_bug1089417.html | 47 + layout/style/test/test_bug1112014.html | 127 + layout/style/test/test_bug1203766.html | 112 + layout/style/test/test_bug1232829.html | 38 + layout/style/test/test_bug1292447.html | 377 + layout/style/test/test_bug160403.html | 73 + 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 | 89 + layout/style/test/test_bug357614.html | 73 + layout/style/test/test_bug363146.html | 62 + layout/style/test/test_bug372770.html | 91 + layout/style/test/test_bug373293.html | 29 + layout/style/test/test_bug377947.html | 107 + layout/style/test/test_bug379440.html | 72 + 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 | 78 + layout/style/test/test_bug418986-2.html | 33 + layout/style/test/test_bug437915.html | 70 + layout/style/test/test_bug450191.html | 64 + layout/style/test/test_bug453896_deck.html | 42 + 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_bug511909.html | 205 + 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 | 212 + layout/style/test/test_bug657143.html | 132 + layout/style/test/test_bug664955.html | 37 + 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 | 192 + layout/style/test/test_bug74880.html | 125 + 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_bug798567.html | 26 + layout/style/test/test_bug798843_pref.html | 57 + 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 | 61 + .../style/test/test_change_hint_optimizations.html | 57 + layout/style/test/test_clip-path_polygon.html | 41 + .../test/test_compute_data_with_start_struct.html | 109 + layout/style/test/test_computed_style.html | 413 + .../test/test_computed_style_min_size_auto.html | 133 + .../style/test/test_computed_style_no_pseudo.html | 44 + layout/style/test/test_computed_style_prefs.html | 99 + layout/style/test/test_condition_text.html | 93 + .../style/test/test_condition_text_assignment.html | 59 + .../test/test_contain_formatting_context.html | 40 + .../test/test_counter_descriptor_storage.html | 267 + layout/style/test/test_counter_style.html | 121 + layout/style/test/test_css_cross_domain.html | 99 + layout/style/test/test_css_eof_handling.html | 278 + 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 + layout/style/test/test_css_supports.html | 134 + layout/style/test/test_css_supports_variables.html | 247 + layout/style/test/test_csslexer.js | 171 + layout/style/test/test_default_bidi_css.html | 79 + layout/style/test/test_default_computed_style.html | 58 + layout/style/test/test_descriptor_storage.html | 119 + .../style/test/test_descriptor_syntax_errors.html | 53 + .../style/test/test_dont_use_document_colors.html | 186 + .../test/test_dynamic_change_causing_reflow.html | 173 + layout/style/test/test_exposed_prop_accessors.html | 41 + layout/style/test/test_extra_inherit_initial.html | 109 + .../test/test_flexbox_child_display_values.xhtml | 189 + .../test/test_flexbox_flex_grow_and_shrink.html | 154 + layout/style/test/test_flexbox_flex_shorthand.html | 280 + 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 | 137 + layout/style/test/test_font_face_parser.html | 382 + layout/style/test/test_font_family_parsing.html | 276 + .../test/test_font_feature_values_parsing.html | 356 + layout/style/test/test_font_loading_api.html | 1897 ++ .../test/test_garbage_at_end_of_declarations.html | 151 + layout/style/test/test_grid_computed_values.html | 106 + .../style/test/test_grid_container_shorthands.html | 282 + layout/style/test/test_grid_item_shorthands.html | 153 + .../test/test_grid_shorthand_serialization.html | 236 + layout/style/test/test_group_insertRule.html | 243 + layout/style/test/test_hover_quirk.html | 82 + .../test/test_html_attribute_computed_values.html | 84 + layout/style/test/test_ident_escaping.html | 56 + layout/style/test/test_inherit_computation.html | 159 + layout/style/test/test_inherit_storage.html | 116 + layout/style/test/test_initial_computation.html | 163 + layout/style/test/test_initial_storage.html | 130 + layout/style/test/test_keyframes_rules.html | 134 + .../test/test_load_events_on_stylesheets.html | 152 + layout/style/test/test_logical_properties.html | 422 + layout/style/test/test_media_queries.html | 845 + layout/style/test/test_media_queries_dynamic.html | 213 + .../style/test/test_media_queries_dynamic_xbl.html | 40 + layout/style/test/test_media_query_list.html | 367 + layout/style/test/test_moz_device_pixel_ratio.html | 77 + layout/style/test/test_namespace_rule.html | 462 + layout/style/test/test_of_type_selectors.xhtml | 98 + layout/style/test/test_page_parser.html | 107 + layout/style/test/test_parse_eof.html | 69 + layout/style/test/test_parse_ident.html | 56 + layout/style/test/test_parse_rule.html | 256 + layout/style/test/test_parse_url.html | 195 + .../test/test_parser_diagnostics_unprintables.html | 220 + layout/style/test/test_pixel_lengths.html | 75 + layout/style/test/test_pointer-events.html | 114 + layout/style/test/test_position_float_display.html | 107 + layout/style/test/test_position_sticky.html | 89 + layout/style/test/test_priority_preservation.html | 141 + layout/style/test/test_property_database.html | 170 + layout/style/test/test_property_syntax_errors.html | 153 + layout/style/test/test_pseudoelement_parsing.html | 43 + layout/style/test/test_pseudoelement_state.html | 164 + .../style/test/test_redundant_font_download.html | 130 + layout/style/test/test_rem_unit.html | 80 + .../test/test_restyles_in_smil_animation.html | 113 + layout/style/test/test_root_node_display.html | 67 + layout/style/test/test_rule_insertion.html | 240 + layout/style/test/test_rule_serialization.html | 53 + layout/style/test/test_rules_out_of_sheets.html | 115 + layout/style/test/test_selectors.html | 1297 ++ .../test/test_selectors_on_anonymous_content.html | 78 + layout/style/test/test_setPropertyWithNull.html | 47 + .../test/test_shorthand_property_getters.html | 268 + .../test/test_specified_value_serialization.html | 105 + 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_supports_rules.html | 88 + .../style/test/test_system_font_serialization.html | 59 + .../test/test_text_decoration_shorthands.html | 93 + 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_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 | 282 + .../style/test/test_transitions_per_property.html | 2565 +++ ...test_transitions_replacement_on_busy_frame.html | 30 + .../test/test_transitions_step_functions.html | 93 + .../test_transitions_with_disabled_properties.html | 28 + layout/style/test/test_unclosed_parentheses.html | 289 + 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 | 59 + layout/style/test/test_units_time.html | 47 + layout/style/test/test_unprefixing_service.html | 93 + .../style/test/test_unprefixing_service_prefs.html | 132 + layout/style/test/test_value_cloning.html | 182 + layout/style/test/test_value_computation.html | 249 + layout/style/test/test_value_storage.html | 352 + .../test/test_variable_serialization_computed.html | 82 + .../test_variable_serialization_specified.html | 117 + layout/style/test/test_variables.html | 125 + layout/style/test/test_video_object_fit.html | 53 + layout/style/test/test_viewport_units.html | 66 + layout/style/test/test_visited_image_loading.html | 67 + .../test/test_visited_image_loading_empty.html | 67 + 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 | 77 + layout/style/test/test_webkit_flex_display.html | 48 + layout/style/test/unprefixing_service_iframe.html | 394 + layout/style/test/unprefixing_service_utils.js | 87 + 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 | 60 + layout/style/test/visited_image_loading_frame.html | 15 + .../test/visited_image_loading_frame_empty.html | 15 + layout/style/test/xbl_bindings.xml | 9 + layout/style/test/xpcshell.ini | 5 + layout/style/xbl-marquee/jar.mn | 8 + layout/style/xbl-marquee/moz.build | 7 + layout/style/xbl-marquee/xbl-marquee.css | 12 + layout/style/xbl-marquee/xbl-marquee.xml | 733 + 713 files changed, 189097 insertions(+) create mode 100644 layout/style/AnimationCollection.cpp create mode 100644 layout/style/AnimationCollection.h create mode 100644 layout/style/AnimationCommon.cpp create mode 100644 layout/style/AnimationCommon.h create mode 100644 layout/style/CSS.cpp create mode 100644 layout/style/CSS.h create mode 100644 layout/style/CSSCalc.h create mode 100644 layout/style/CSSEnabledState.h create mode 100644 layout/style/CSSLexer.cpp create mode 100644 layout/style/CSSLexer.h create mode 100644 layout/style/CSSRuleList.cpp create mode 100644 layout/style/CSSRuleList.h create mode 100644 layout/style/CSSStyleSheet.cpp create mode 100644 layout/style/CSSStyleSheet.h create mode 100644 layout/style/CSSUnprefixingService.js create mode 100644 layout/style/CSSUnprefixingService.manifest create mode 100644 layout/style/CSSValue.h create mode 100644 layout/style/CSSVariableDeclarations.cpp create mode 100644 layout/style/CSSVariableDeclarations.h create mode 100644 layout/style/CSSVariableImageTable.h create mode 100644 layout/style/CSSVariableResolver.cpp create mode 100644 layout/style/CSSVariableResolver.h create mode 100644 layout/style/CSSVariableValues.cpp create mode 100644 layout/style/CSSVariableValues.h create mode 100644 layout/style/CounterStyleManager.cpp create mode 100644 layout/style/CounterStyleManager.h create mode 100644 layout/style/Declaration.cpp create mode 100644 layout/style/Declaration.h create mode 100644 layout/style/DeclarationBlock.h create mode 100644 layout/style/DeclarationBlockInlines.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/FontFaceSet.cpp create mode 100644 layout/style/FontFaceSet.h create mode 100644 layout/style/FontFaceSetIterator.cpp create mode 100644 layout/style/FontFaceSetIterator.h create mode 100644 layout/style/GenerateCSSPropsGenerated.py create mode 100644 layout/style/GroupRule.h create mode 100644 layout/style/HandleRefPtr.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/ImportRule.h create mode 100644 layout/style/IncrementalClearCOMRuleArray.cpp create mode 100644 layout/style/IncrementalClearCOMRuleArray.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/Makefile.in create mode 100644 layout/style/MediaQueryList.cpp create mode 100644 layout/style/MediaQueryList.h create mode 100644 layout/style/NameSpaceRule.h create mode 100644 layout/style/PythonCSSProps.h create mode 100644 layout/style/Rule.h create mode 100644 layout/style/RuleNodeCacheConditions.cpp create mode 100644 layout/style/RuleNodeCacheConditions.h create mode 100644 layout/style/RuleProcessorCache.cpp create mode 100644 layout/style/RuleProcessorCache.h create mode 100644 layout/style/SVGAttrAnimationRuleProcessor.cpp create mode 100644 layout/style/SVGAttrAnimationRuleProcessor.h create mode 100644 layout/style/ServoBindingList.h create mode 100644 layout/style/ServoBindingTypes.h create mode 100644 layout/style/ServoBindings.cpp create mode 100644 layout/style/ServoBindings.h create mode 100644 layout/style/ServoDeclarationBlock.cpp create mode 100644 layout/style/ServoDeclarationBlock.h create mode 100644 layout/style/ServoElementSnapshot.cpp create mode 100644 layout/style/ServoElementSnapshot.h create mode 100644 layout/style/ServoStyleSet.cpp create mode 100644 layout/style/ServoStyleSet.h create mode 100644 layout/style/ServoStyleSheet.cpp create mode 100644 layout/style/ServoStyleSheet.h create mode 100644 layout/style/ServoTypes.h create mode 100644 layout/style/ServoUtils.h create mode 100644 layout/style/SheetParsingMode.h create mode 100644 layout/style/SheetType.h create mode 100644 layout/style/StyleAnimationValue.cpp create mode 100644 layout/style/StyleAnimationValue.h create mode 100644 layout/style/StyleBackendType.h create mode 100644 layout/style/StyleComplexColor.h create mode 100644 layout/style/StyleContextSource.h create mode 100644 layout/style/StyleRule.cpp create mode 100644 layout/style/StyleRule.h create mode 100644 layout/style/StyleSetHandle.h create mode 100644 layout/style/StyleSetHandleInlines.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/StyleStructContext.h create mode 100644 layout/style/TopLevelImageDocument.css create mode 100644 layout/style/TopLevelVideoDocument.css 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/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/1227501-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/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/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/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/1321357-1.html create mode 100644 layout/style/crashtests/1356601-1.html create mode 100644 layout/style/crashtests/147777-1.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.xul create mode 100644 layout/style/crashtests/418007-1.xhtml create mode 100644 layout/style/crashtests/431705-1.xul 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/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/621596-1.html create mode 100644 layout/style/crashtests/622314-1.xhtml create mode 100644 layout/style/crashtests/637242.xhtml create mode 100644 layout/style/crashtests/645142.html create mode 100644 layout/style/crashtests/645951-1-ref.html create mode 100644 layout/style/crashtests/645951-1.css create mode 100644 layout/style/crashtests/645951-1.html create mode 100644 layout/style/crashtests/652976-1.svg 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/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/812824.html create mode 100644 layout/style/crashtests/822766-1.html create mode 100644 layout/style/crashtests/822842.html create mode 100644 layout/style/crashtests/827591-1.html create mode 100644 layout/style/crashtests/829817.html create mode 100644 layout/style/crashtests/840898.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/880862.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/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/long-url-list-stack-overflow.html create mode 100644 layout/style/designmode.css create mode 100755 layout/style/generate-stylestructlist.py 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/nsCSSDataBlock.cpp create mode 100644 layout/style/nsCSSDataBlock.h create mode 100644 layout/style/nsCSSFontDescList.h create mode 100644 layout/style/nsCSSKeywordList.h create mode 100644 layout/style/nsCSSKeywords.cpp create mode 100644 layout/style/nsCSSKeywords.h create mode 100644 layout/style/nsCSSParser.cpp create mode 100644 layout/style/nsCSSParser.h create mode 100644 layout/style/nsCSSPropAliasList.h create mode 100644 layout/style/nsCSSPropList.h create mode 100644 layout/style/nsCSSPropLogicalGroupList.h create mode 100644 layout/style/nsCSSPropertyID.h 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/nsCSSPropsGenerated.inc.in create mode 100644 layout/style/nsCSSPseudoClassList.h create mode 100644 layout/style/nsCSSPseudoClasses.cpp create mode 100644 layout/style/nsCSSPseudoClasses.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/nsCSSRuleProcessor.cpp create mode 100644 layout/style/nsCSSRuleProcessor.h create mode 100644 layout/style/nsCSSRules.cpp create mode 100644 layout/style/nsCSSRules.h create mode 100644 layout/style/nsCSSScanner.cpp create mode 100644 layout/style/nsCSSScanner.h create mode 100644 layout/style/nsCSSValue.cpp create mode 100644 layout/style/nsCSSValue.h create mode 100644 layout/style/nsComputedDOMStyle.cpp create mode 100644 layout/style/nsComputedDOMStyle.h create mode 100644 layout/style/nsComputedDOMStylePropertyList.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/nsDOMCSSRGBColor.cpp create mode 100644 layout/style/nsDOMCSSRGBColor.h create mode 100644 layout/style/nsDOMCSSRect.cpp create mode 100644 layout/style/nsDOMCSSRect.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/nsHTMLCSSStyleSheet.cpp create mode 100644 layout/style/nsHTMLCSSStyleSheet.h create mode 100644 layout/style/nsHTMLStyleSheet.cpp create mode 100644 layout/style/nsHTMLStyleSheet.h create mode 100644 layout/style/nsICSSDeclaration.h create mode 100644 layout/style/nsICSSLoaderObserver.h create mode 100644 layout/style/nsICSSPseudoComparator.h create mode 100644 layout/style/nsICSSStyleRuleDOMWrapper.h create mode 100644 layout/style/nsICSSUnprefixingService.idl create mode 100644 layout/style/nsIMediaList.h create mode 100644 layout/style/nsIStyleRule.h create mode 100644 layout/style/nsIStyleRuleProcessor.h create mode 100644 layout/style/nsLayoutStylesheetCache.cpp create mode 100644 layout/style/nsLayoutStylesheetCache.h create mode 100644 layout/style/nsMediaFeatures.cpp create mode 100644 layout/style/nsMediaFeatures.h create mode 100644 layout/style/nsNthIndexCache.cpp create mode 100644 layout/style/nsNthIndexCache.h create mode 100644 layout/style/nsROCSSPrimitiveValue.cpp create mode 100644 layout/style/nsROCSSPrimitiveValue.h create mode 100644 layout/style/nsRuleData.cpp create mode 100644 layout/style/nsRuleData.h create mode 100644 layout/style/nsRuleNode.cpp create mode 100644 layout/style/nsRuleNode.h create mode 100644 layout/style/nsRuleProcessorData.h create mode 100644 layout/style/nsRuleWalker.h create mode 100644 layout/style/nsStyleConsts.h create mode 100644 layout/style/nsStyleContext.cpp create mode 100644 layout/style/nsStyleContext.h create mode 100644 layout/style/nsStyleCoord.cpp create mode 100644 layout/style/nsStyleCoord.h create mode 100644 layout/style/nsStyleSet.cpp create mode 100644 layout/style/nsStyleSet.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/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/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@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@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/arrow-left.gif create mode 100644 layout/style/res/arrow-right.gif create mode 100644 layout/style/res/arrow.gif create mode 100644 layout/style/res/arrowd-left.gif create mode 100644 layout/style/res/arrowd-right.gif create mode 100644 layout/style/res/arrowd.gif create mode 100644 layout/style/res/counterstyles.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/noscript.css create mode 100644 layout/style/res/number-control.css create mode 100644 layout/style/res/plaintext.css create mode 100644 layout/style/res/quirk.css create mode 100644 layout/style/res/ua.css create mode 100644 layout/style/res/viewsource.css 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/TestCSSPropertyLookup.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.ini create mode 100644 layout/style/test/browser_bug453896.js create mode 100644 layout/style/test/browser_newtab_share_rule_processors.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.xul create mode 100644 layout/style/test/chrome/chrome.ini 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/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_author_specified_style.html create mode 100644 layout/style/test/chrome/test_bug1157097.html create mode 100644 layout/style/test/chrome/test_bug1160724.xul create mode 100644 layout/style/test/chrome/test_bug418986-2.xul create mode 100644 layout/style/test/chrome/test_bug535806.xul 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/css_properties_like_longhand.js create mode 100644 layout/style/test/descriptor_database.js create mode 100644 layout/style/test/display_mode_reflow_iframe.html 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_effect_timing_duration.html create mode 100644 layout/style/test/file_animations_effect_timing_enddelay.html create mode 100644 layout/style/test/file_animations_effect_timing_iterations.html create mode 100644 layout/style/test/file_animations_iterationstart.html create mode 100644 layout/style/test/file_animations_pausing.html create mode 100644 layout/style/test/file_animations_playbackrate.html create mode 100644 layout/style/test/file_animations_styles_on_event.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_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_font_loading_api_vframe.html create mode 100644 layout/style/test/file_transitions_replacement_on_busy_frame.html create mode 100644 layout/style/test/file_transitions_with_disabled_properties.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/media_queries_dynamic_xbl_binding.xml create mode 100644 layout/style/test/media_queries_dynamic_xbl_iframe.html create mode 100644 layout/style/test/media_queries_dynamic_xbl_style.css create mode 100644 layout/style/test/media_queries_iframe.html create mode 100644 layout/style/test/mochitest.ini create mode 100644 layout/style/test/moz.build 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/newtab_share_rule_processors.html 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/style_attribute_tests.js 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_align_shorthand_serialization.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_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_styles_on_event.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_asyncopen2.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_background_blend_mode.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_bug160403.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_bug453896_deck.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_bug511909.html 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_bug664955.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_bug798567.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_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_min_size_auto.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_condition_text_assignment.html create mode 100644 layout/style/test/test_contain_formatting_context.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_css_cross_domain.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_supports.html create mode 100644 layout/style/test/test_css_supports_variables.html create mode 100644 layout/style/test/test_csslexer.js 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_dont_use_document_colors.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_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_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_font_face_parser.html create mode 100644 layout/style/test/test_font_family_parsing.html create mode 100644 layout/style/test/test_font_feature_values_parsing.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_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_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_keyframes_rules.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_media_queries.html create mode 100644 layout/style/test/test_media_queries_dynamic.html create mode 100644 layout/style/test/test_media_queries_dynamic_xbl.html create mode 100644 layout/style/test/test_media_query_list.html create mode 100644 layout/style/test/test_moz_device_pixel_ratio.html create mode 100644 layout/style/test/test_namespace_rule.html create mode 100644 layout/style/test/test_of_type_selectors.xhtml 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_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_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_pseudoelement_parsing.html create mode 100644 layout/style/test/test_pseudoelement_state.html create mode 100644 layout/style/test/test_redundant_font_download.html create mode 100644 layout/style/test/test_rem_unit.html create mode 100644 layout/style/test/test_restyles_in_smil_animation.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_rule_serialization.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_selectors_on_anonymous_content.html create mode 100644 layout/style/test/test_setPropertyWithNull.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_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_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_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_step_functions.html create mode 100644 layout/style/test/test_transitions_with_disabled_properties.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_unprefixing_service.html create mode 100644 layout/style/test/test_unprefixing_service_prefs.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_video_object_fit.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/unprefixing_service_iframe.html create mode 100644 layout/style/test/unprefixing_service_utils.js 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/test/xbl_bindings.xml create mode 100644 layout/style/test/xpcshell.ini create mode 100644 layout/style/xbl-marquee/jar.mn create mode 100644 layout/style/xbl-marquee/moz.build create mode 100644 layout/style/xbl-marquee/xbl-marquee.css create mode 100644 layout/style/xbl-marquee/xbl-marquee.xml (limited to 'layout/style') diff --git a/layout/style/AnimationCollection.cpp b/layout/style/AnimationCollection.cpp new file mode 100644 index 000000000..f7826f5ed --- /dev/null +++ b/layout/style/AnimationCollection.cpp @@ -0,0 +1,172 @@ +/* -*- 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 "mozilla/RestyleManagerHandle.h" +#include "mozilla/RestyleManagerHandleInlines.h" +#include "nsAnimationManager.h" // For dom::CSSAnimation +#include "nsPresContext.h" +#include "nsTransitionManager.h" // For dom::CSSTransition + +namespace mozilla { + +template +/* static */ void +AnimationCollection::PropertyDtor(void* aObject, + nsIAtom* aPropertyName, + void* aPropertyValue, + void* aData) +{ + AnimationCollection* collection = + static_cast(aPropertyValue); +#ifdef DEBUG + MOZ_ASSERT(!collection->mCalledPropertyDtor, "can't call dtor twice"); + collection->mCalledPropertyDtor = true; +#endif + { + nsAutoAnimationMutationBatch mb(collection->mElement->OwnerDoc()); + + for (size_t animIdx = collection->mAnimations.Length(); animIdx-- != 0; ) { + collection->mAnimations[animIdx]->CancelFromStyle(); + } + } + delete collection; +} + +template +/* static */ AnimationCollection* +AnimationCollection::GetAnimationCollection( + dom::Element *aElement, + CSSPseudoElementType aPseudoType) +{ + if (!aElement->MayHaveAnimations()) { + // Early return for the most common case. + return nullptr; + } + + nsIAtom* propName = GetPropertyAtomForPseudoType(aPseudoType); + if (!propName) { + return nullptr; + } + + return + static_cast*>(aElement-> + GetProperty(propName)); +} + +template +/* static */ AnimationCollection* +AnimationCollection::GetAnimationCollection( + const nsIFrame* aFrame) +{ + Maybe pseudoElement = + EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame); + if (!pseudoElement) { + return nullptr; + } + + if (!pseudoElement->mElement->MayHaveAnimations()) { + return nullptr; + } + + return GetAnimationCollection(pseudoElement->mElement, + pseudoElement->mPseudoType); +} + +template +/* static */ AnimationCollection* +AnimationCollection::GetOrCreateAnimationCollection( + dom::Element* aElement, + CSSPseudoElementType aPseudoType, + bool* aCreatedCollection) +{ + MOZ_ASSERT(aCreatedCollection); + *aCreatedCollection = false; + + nsIAtom* propName = GetPropertyAtomForPseudoType(aPseudoType); + MOZ_ASSERT(propName, "Should only try to create animations for one of the" + " recognized pseudo types"); + + auto collection = static_cast*>( + aElement->GetProperty(propName)); + if (!collection) { + // FIXME: Consider arena-allocating? + collection = new AnimationCollection(aElement, propName); + nsresult rv = + aElement->SetProperty(propName, collection, + &AnimationCollection::PropertyDtor, + false); + if (NS_FAILED(rv)) { + NS_WARNING("SetProperty failed"); + // The collection must be destroyed via PropertyDtor, otherwise + // mCalledPropertyDtor assertion is triggered in destructor. + AnimationCollection::PropertyDtor(aElement, propName, + collection, nullptr); + return nullptr; + } + + *aCreatedCollection = true; + aElement->SetMayHaveAnimations(); + } + + return collection; +} + +template +/* static */ nsString +AnimationCollection::PseudoTypeAsString( + CSSPseudoElementType aPseudoType) +{ + switch (aPseudoType) { + case CSSPseudoElementType::before: + return NS_LITERAL_STRING("::before"); + case CSSPseudoElementType::after: + return NS_LITERAL_STRING("::after"); + default: + MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo, + "Unexpected pseudo type"); + return EmptyString(); + } +} + +template +void +AnimationCollection::UpdateCheckGeneration( + nsPresContext* aPresContext) +{ + if (aPresContext->RestyleManager()->IsServo()) { + // stylo: ServoRestyleManager does not support animations yet. + return; + } + mCheckGeneration = + aPresContext->RestyleManager()->AsGecko()->GetAnimationGeneration(); +} + +template +/*static*/ nsIAtom* +AnimationCollection::GetPropertyAtomForPseudoType( + CSSPseudoElementType aPseudoType) +{ + nsIAtom* propName = nullptr; + + if (aPseudoType == CSSPseudoElementType::NotPseudo) { + propName = TraitsType::ElementPropertyAtom(); + } else if (aPseudoType == CSSPseudoElementType::before) { + propName = TraitsType::BeforePropertyAtom(); + } else if (aPseudoType == CSSPseudoElementType::after) { + propName = TraitsType::AfterPropertyAtom(); + } + + return propName; +} + +// 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 000000000..96163fcc8 --- /dev/null +++ b/layout/style/AnimationCollection.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_AnimationCollection_h +#define mozilla_AnimationCollection_h + +#include "mozilla/dom/Animation.h" +#include "mozilla/dom/Element.h" +#include "mozilla/Assertions.h" +#include "mozilla/LinkedList.h" +#include "mozilla/RefPtr.h" +#include "nsCSSPseudoElements.h" +#include "nsDOMMutationObserver.h" +#include "nsTArray.h" + +class nsIAtom; +class nsPresContext; + +namespace mozilla { + +// Traits class to define the specific atoms used when storing specializations +// of AnimationCollection as a property on an Element (e.g. which atom +// to use when storing an AnimationCollection for a ::before +// pseudo-element). +template +struct AnimationTypeTraits { }; + +template +class AnimationCollection + : public LinkedListElement> +{ + typedef AnimationCollection SelfType; + typedef AnimationTypeTraits TraitsType; + + AnimationCollection(dom::Element* aElement, nsIAtom* aElementProperty) + : mElement(aElement) + , mElementProperty(aElementProperty) + , mCheckGeneration(0) +#ifdef DEBUG + , mCalledPropertyDtor(false) +#endif + { + MOZ_COUNT_CTOR(AnimationCollection); + } + +public: + ~AnimationCollection() + { + MOZ_ASSERT(mCalledPropertyDtor, + "must call destructor through element property dtor"); + MOZ_COUNT_DTOR(AnimationCollection); + LinkedListElement::remove(); + } + + void Destroy() + { + // This will call our destructor. + mElement->DeleteProperty(mElementProperty); + } + + static void PropertyDtor(void *aObject, nsIAtom *aPropertyName, + void *aPropertyValue, void *aData); + + // Get the collection of animations for the given |aElement| and + // |aPseudoType|. + static AnimationCollection* + GetAnimationCollection(dom::Element* aElement, + CSSPseudoElementType aPseudoType); + + // 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* GetAnimationCollection( + const nsIFrame* aFrame); + + // Get the collection of animations for the given |aElement| and + // |aPseudoType| or create it if it does not already exist. + // + // We'll set the outparam |aCreatedCollection| to true if we have + // to create the collection and we successfully do so. Otherwise, + // we'll set it to false. + static AnimationCollection* + GetOrCreateAnimationCollection(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + bool* aCreatedCollection); + + bool IsForElement() const { // rather than for a pseudo-element + return mElementProperty == TraitsType::ElementPropertyAtom(); + } + + bool IsForBeforePseudo() const { + return mElementProperty == TraitsType::BeforePropertyAtom(); + } + + bool IsForAfterPseudo() const { + return mElementProperty == TraitsType::AfterPropertyAtom(); + } + + CSSPseudoElementType PseudoElementType() const + { + if (IsForElement()) { + return CSSPseudoElementType::NotPseudo; + } + if (IsForBeforePseudo()) { + return CSSPseudoElementType::before; + } + MOZ_ASSERT(IsForAfterPseudo(), + "::before & ::after should be the only pseudo-elements here"); + return CSSPseudoElementType::after; + } + + static nsString PseudoTypeAsString(CSSPseudoElementType aPseudoType); + + dom::Element *mElement; + + // the atom we use in mElement's prop table (must be a static atom, + // i.e., in an atom list) + nsIAtom *mElementProperty; + + InfallibleTArray> mAnimations; + + // For CSS transitions only, we record the most recent generation + // for which we've done the transition update, so that we avoid doing + // it more than once per style change. + // (Note that we also store an animation generation on each EffectSet in + // order to track when we need to update animations on layers.) + uint64_t mCheckGeneration; + + // Update mCheckGeneration to RestyleManager's count + void UpdateCheckGeneration(nsPresContext* aPresContext); + +private: + static nsIAtom* GetPropertyAtomForPseudoType( + CSSPseudoElementType aPseudoType); + +#ifdef DEBUG + bool mCalledPropertyDtor; +#endif +}; + +} // namespace mozilla + +#endif // mozilla_AnimationCollection_h diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp new file mode 100644 index 000000000..f4ebc2a93 --- /dev/null +++ b/layout/style/AnimationCommon.cpp @@ -0,0 +1,54 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 "AnimationCommon.h" +#include "nsTransitionManager.h" +#include "nsAnimationManager.h" + +#include "ActiveLayerTracker.h" +#include "gfxPlatform.h" +#include "nsCSSPropertyIDSet.h" +#include "nsCSSValue.h" +#include "nsCycleCollectionParticipant.h" +#include "nsStyleContext.h" +#include "nsIFrame.h" +#include "nsLayoutUtils.h" +#include "FrameLayerBuilder.h" +#include "nsDisplayList.h" +#include "mozilla/AnimationUtils.h" +#include "mozilla/EffectCompositor.h" +#include "mozilla/EffectSet.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/dom/KeyframeEffectReadOnly.h" +#include "nsRuleProcessorData.h" +#include "nsStyleSet.h" +#include "nsStyleChangeList.h" + +using mozilla::dom::Animation; +using mozilla::dom::KeyframeEffectReadOnly; + +namespace mozilla { + +nsPresContext* +OwningElementRef::GetRenderedPresContext() const +{ + if (!mElement) { + return nullptr; + } + + nsIDocument* doc = mElement->GetComposedDoc(); + if (!doc) { + return nullptr; + } + + nsIPresShell* shell = doc->GetShell(); + if (!shell) { + return nullptr; + } + + return shell->GetPresContext(); +} + +} // namespace mozilla diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h new file mode 100644 index 000000000..37030411c --- /dev/null +++ b/layout/style/AnimationCommon.h @@ -0,0 +1,256 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 // For +#include "mozilla/AnimationCollection.h" +#include "mozilla/AnimationComparator.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/LinkedList.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/dom/Animation.h" +#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF +#include "mozilla/Assertions.h" +#include "nsContentUtils.h" +#include "nsCSSPseudoElements.h" +#include "nsCycleCollectionParticipant.h" + +class nsIFrame; +class nsPresContext; + +namespace mozilla { + +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; + } + +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() + : mElement(nullptr) + , mPseudoType(CSSPseudoElementType::NotPseudo) + { } + + OwningElementRef(dom::Element& aElement, + CSSPseudoElementType aPseudoType) + : mElement(&aElement) + , mPseudoType(aPseudoType) + { } + + bool Equals(const OwningElementRef& aOther) const + { + return mElement == aOther.mElement && + mPseudoType == aOther.mPseudoType; + } + + bool LessThan(const OwningElementRef& aOther) const + { + MOZ_ASSERT(mElement && aOther.mElement, + "Elements to compare should not be null"); + + if (mElement != aOther.mElement) { + return nsContentUtils::PositionIsBefore(mElement, aOther.mElement); + } + + return mPseudoType == CSSPseudoElementType::NotPseudo || + (mPseudoType == CSSPseudoElementType::before && + aOther.mPseudoType == CSSPseudoElementType::after); + } + + bool IsSet() const { return !!mElement; } + + void GetElement(dom::Element*& aElement, + CSSPseudoElementType& aPseudoType) const { + aElement = mElement; + aPseudoType = mPseudoType; + } + + nsPresContext* GetRenderedPresContext() const; + +private: + dom::Element* MOZ_NON_OWNING_REF mElement; + CSSPseudoElementType mPseudoType; +}; + +template +class DelayedEventDispatcher +{ +public: + DelayedEventDispatcher() : mIsSorted(true) { } + + void QueueEvent(EventInfo&& aEventInfo) + { + mPendingEvents.AppendElement(Forward(aEventInfo)); + mIsSorted = false; + } + + // This is exposed as a separate method so that when we are dispatching + // *both* transition events and animation events we can sort both lists + // once using the current state of the document before beginning any + // dispatch. + void SortEvents() + { + if (mIsSorted) { + return; + } + + // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is + // fixed. + std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(), + EventInfoLessThan()); + mIsSorted = true; + } + + // Takes a reference to the owning manager's pres context so it can + // detect if the pres context is destroyed while dispatching one of + // the events. + // + // This will call SortEvents automatically if it has not already been + // called. + void DispatchEvents(nsPresContext* const & aPresContext) + { + if (!aPresContext || mPendingEvents.IsEmpty()) { + return; + } + + SortEvents(); + + EventArray events; + mPendingEvents.SwapElements(events); + // mIsSorted will be set to true by SortEvents above, and we leave it + // that way since mPendingEvents is now empty + for (EventInfo& info : events) { + EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent); + + if (!aPresContext) { + break; + } + } + } + + void ClearEventQueue() + { + mPendingEvents.Clear(); + mIsSorted = true; + } + bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } + + // Methods for supporting cycle-collection + void Traverse(nsCycleCollectionTraversalCallback* aCallback, + const char* aName) + { + for (EventInfo& info : mPendingEvents) { + ImplCycleCollectionTraverse(*aCallback, info.mElement, aName); + ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName); + } + } + void Unlink() { ClearEventQueue(); } + +protected: + class EventInfoLessThan + { + public: + bool operator()(const EventInfo& a, const EventInfo& b) const + { + if (a.mTimeStamp != b.mTimeStamp) { + // Null timestamps sort first + if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) { + return a.mTimeStamp.IsNull(); + } else { + return a.mTimeStamp < b.mTimeStamp; + } + } + + AnimationPtrComparator> comparator; + return comparator.LessThan(a.mAnimation, b.mAnimation); + } + }; + + typedef nsTArray EventArray; + EventArray mPendingEvents; + bool mIsSorted; +}; + +template +inline void +ImplCycleCollectionUnlink(DelayedEventDispatcher& aField) +{ + aField.Unlink(); +} + +template +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, + DelayedEventDispatcher& aField, + const char* aName, + uint32_t aFlags = 0) +{ + aField.Traverse(&aCallback, aName); +} + +} // namespace mozilla + +#endif /* !defined(mozilla_css_AnimationCommon_h) */ diff --git a/layout/style/CSS.cpp b/layout/style/CSS.cpp new file mode 100644 index 000000000..b70f67a50 --- /dev/null +++ b/layout/style/CSS.cpp @@ -0,0 +1,107 @@ +/* -*- 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/. */ + +/* DOM object holding utility CSS functions */ + +#include "CSS.h" + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/ServoBindings.h" +#include "nsCSSParser.h" +#include "nsGlobalWindow.h" +#include "nsIDocument.h" +#include "nsIURI.h" +#include "nsStyleUtil.h" +#include "xpcpublic.h" + +namespace mozilla { +namespace dom { + +struct SupportsParsingInfo +{ + nsIURI* mDocURI; + nsIURI* mBaseURI; + nsIPrincipal* mPrincipal; + StyleBackendType mStyleBackendType; +}; + +static nsresult +GetParsingInfo(const GlobalObject& aGlobal, + SupportsParsingInfo& aInfo) +{ + nsGlobalWindow* win = xpc::WindowOrNull(aGlobal.Get()); + if (!win) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr doc = win->GetDoc(); + if (!doc) { + return NS_ERROR_FAILURE; + } + + aInfo.mDocURI = nsCOMPtr(doc->GetDocumentURI()).get(); + aInfo.mBaseURI = nsCOMPtr(doc->GetBaseURI()).get(); + aInfo.mPrincipal = win->GetPrincipal(); + aInfo.mStyleBackendType = doc->GetStyleBackendType(); + return NS_OK; +} + +/* static */ bool +CSS::Supports(const GlobalObject& aGlobal, + const nsAString& aProperty, + const nsAString& aValue, + ErrorResult& aRv) +{ + SupportsParsingInfo info; + + nsresult rv = GetParsingInfo(aGlobal, info); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return false; + } + + if (info.mStyleBackendType == StyleBackendType::Servo) { + NS_ConvertUTF16toUTF8 property(aProperty); + NS_ConvertUTF16toUTF8 value(aValue); + return Servo_CSSSupports(&property, &value); + } + + nsCSSParser parser; + return parser.EvaluateSupportsDeclaration(aProperty, aValue, info.mDocURI, + info.mBaseURI, info.mPrincipal); +} + +/* static */ bool +CSS::Supports(const GlobalObject& aGlobal, + const nsAString& aCondition, + ErrorResult& aRv) +{ + SupportsParsingInfo info; + + nsresult rv = GetParsingInfo(aGlobal, info); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return false; + } + + if (info.mStyleBackendType == StyleBackendType::Servo) { + MOZ_CRASH("stylo: CSS.supports() with arguments is not yet implemented"); + } + + nsCSSParser parser; + return parser.EvaluateSupportsCondition(aCondition, info.mDocURI, + info.mBaseURI, info.mPrincipal); +} + +/* static */ void +CSS::Escape(const GlobalObject& aGlobal, + const nsAString& aIdent, + nsAString& aReturn) +{ + nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/CSS.h b/layout/style/CSS.h new file mode 100644 index 000000000..ce3e38da4 --- /dev/null +++ b/layout/style/CSS.h @@ -0,0 +1,44 @@ +/* -*- 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/. */ + +/* DOM object holding utility CSS functions */ + +#ifndef mozilla_dom_CSS_h_ +#define mozilla_dom_CSS_h_ + +#include "mozilla/Attributes.h" +#include "mozilla/Preferences.h" + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; + +class CSS { +private: + CSS() = delete; + +public: + static bool Supports(const GlobalObject& aGlobal, + const nsAString& aProperty, + const nsAString& aValue, + ErrorResult& aRv); + + static bool Supports(const GlobalObject& aGlobal, + const nsAString& aDeclaration, + ErrorResult& aRv); + + static void Escape(const GlobalObject& aGlobal, + const nsAString& aIdent, + nsAString& aReturn); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CSS_h_ diff --git a/layout/style/CSSCalc.h b/layout/style/CSSCalc.h new file mode 100644 index 000000000..141ca9c0a --- /dev/null +++ b/layout/style/CSSCalc.h @@ -0,0 +1,361 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 CSSCalc_h_ +#define CSSCalc_h_ + +#include "nsCSSValue.h" +#include "nsStyleCoord.h" +#include + +namespace mozilla { + +namespace css { + +/** + * ComputeCalc computes the result of a calc() expression tree. + * + * It is templatized over a CalcOps class that is expected to provide: + * + * // input_type and input_array_type have a bunch of very specific + * // expectations (which happen to be met by two classes (nsCSSValue + * // and nsStyleCoord). There must be methods (roughly): + * // input_array_type* input_type::GetArrayValue(); + * // uint32_t input_array_type::Count() const; + * // input_type& input_array_type::Item(uint32_t); + * typedef ... input_type; + * typedef ... input_array_type; + * + * typedef ... result_type; + * + * // GetUnit(avalue) must return the correct nsCSSUnit for any + * // value that represents a calc tree node (eCSSUnit_Calc*). For + * // other nodes, it may return any non eCSSUnit_Calc* unit. + * static nsCSSUnit GetUnit(const input_type& aValue); + * + * result_type + * MergeAdditive(nsCSSUnit aCalcFunction, + * result_type aValue1, result_type aValue2); + * + * result_type + * MergeMultiplicativeL(nsCSSUnit aCalcFunction, + * float aValue1, result_type aValue2); + * + * result_type + * MergeMultiplicativeR(nsCSSUnit aCalcFunction, + * result_type aValue1, float aValue2); + * + * result_type + * ComputeLeafValue(const input_type& aValue); + * + * float + * ComputeNumber(const input_type& aValue); + * + * The CalcOps methods might compute the calc() expression down to a + * number, reduce some parts of it to a number but replicate other + * parts, or produce a tree with a different data structure (for + * example, nsCSS* for specified values vs nsStyle* for computed + * values). + * + * For each leaf in the calc() expression, ComputeCalc will call either + * ComputeNumber (when the leaf is the left side of a Times_L or the + * right side of a Times_R or Divided) or ComputeLeafValue (otherwise). + * (The CalcOps in the CSS parser that reduces purely numeric + * expressions in turn calls ComputeCalc on numbers; other ops can + * presume that expressions in the number positions have already been + * normalized to a single numeric value and derive from + * NumbersAlreadyNormalizedCalcOps.) + * + * For non-leaves, one of the Merge functions will be called: + * MergeAdditive for Plus and Minus + * MergeMultiplicativeL for Times_L (number * value) + * MergeMultiplicativeR for Times_R (value * number) and Divided + */ +template +static typename CalcOps::result_type +ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) +{ + switch (CalcOps::GetUnit(aValue)) { + case eCSSUnit_Calc: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() == 1, "unexpected length"); + return ComputeCalc(arr->Item(0), aOps); + } + case eCSSUnit_Calc_Plus: + case eCSSUnit_Calc_Minus: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() == 2, "unexpected length"); + typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps), + rhs = ComputeCalc(arr->Item(1), aOps); + return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs); + } + case eCSSUnit_Calc_Times_L: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() == 2, "unexpected length"); + float lhs = aOps.ComputeNumber(arr->Item(0)); + typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps); + return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs); + } + case eCSSUnit_Calc_Times_R: + case eCSSUnit_Calc_Divided: { + typename CalcOps::input_array_type *arr = aValue.GetArrayValue(); + MOZ_ASSERT(arr->Count() == 2, "unexpected length"); + typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps); + float rhs = aOps.ComputeNumber(arr->Item(1)); + return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs); + } + default: { + return aOps.ComputeLeafValue(aValue); + } + } +} + +/** + * The input unit operation for input_type being nsCSSValue. + */ +struct CSSValueInputCalcOps +{ + typedef nsCSSValue input_type; + typedef nsCSSValue::Array input_array_type; + + static nsCSSUnit GetUnit(const nsCSSValue& aValue) + { + return aValue.GetUnit(); + } + +}; + +/** + * Basic*CalcOps provide a partial implementation of the CalcOps + * template parameter to ComputeCalc, for those callers whose merging + * just consists of mathematics (rather than tree construction). + */ + +struct BasicCoordCalcOps +{ + typedef nscoord result_type; + + result_type + MergeAdditive(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + if (aCalcFunction == eCSSUnit_Calc_Plus) { + return NSCoordSaturatingAdd(aValue1, aValue2); + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, + "unexpected unit"); + return NSCoordSaturatingSubtract(aValue1, aValue2, 0); + } + + result_type + MergeMultiplicativeL(nsCSSUnit aCalcFunction, + float aValue1, result_type aValue2) + { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, + "unexpected unit"); + return NSCoordSaturatingMultiply(aValue2, aValue1); + } + + result_type + MergeMultiplicativeR(nsCSSUnit aCalcFunction, + result_type aValue1, float aValue2) + { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R || + aCalcFunction == eCSSUnit_Calc_Divided, + "unexpected unit"); + if (aCalcFunction == eCSSUnit_Calc_Divided) { + aValue2 = 1.0f / aValue2; + } + return NSCoordSaturatingMultiply(aValue1, aValue2); + } +}; + +struct BasicFloatCalcOps +{ + typedef float result_type; + + result_type + MergeAdditive(nsCSSUnit aCalcFunction, + result_type aValue1, result_type aValue2) + { + if (aCalcFunction == eCSSUnit_Calc_Plus) { + return aValue1 + aValue2; + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, + "unexpected unit"); + return aValue1 - aValue2; + } + + result_type + MergeMultiplicativeL(nsCSSUnit aCalcFunction, + float aValue1, result_type aValue2) + { + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, + "unexpected unit"); + return aValue1 * aValue2; + } + + result_type + MergeMultiplicativeR(nsCSSUnit aCalcFunction, + result_type aValue1, float aValue2) + { + if (aCalcFunction == eCSSUnit_Calc_Times_R) { + return aValue1 * aValue2; + } + MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided, + "unexpected unit"); + return aValue1 / aValue2; + } +}; + +/** + * A ComputeNumber implementation for callers that can assume numbers + * are already normalized (i.e., anything past the parser). + */ +struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps +{ + float ComputeNumber(const nsCSSValue& aValue) + { + MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); + return aValue.GetFloatValue(); + } +}; + +/** + * SerializeCalc appends the serialization of aValue to a string. + * + * It is templatized over a CalcOps class that is expected to provide: + * + * // input_type and input_array_type have a bunch of very specific + * // expectations (which happen to be met by two classes (nsCSSValue + * // and nsStyleCoord). There must be methods (roughly): + * // input_array_type* input_type::GetArrayValue(); + * // uint32_t input_array_type::Count() const; + * // input_type& input_array_type::Item(uint32_t); + * typedef ... input_type; + * typedef ... input_array_type; + * + * static nsCSSUnit GetUnit(const input_type& aValue); + * + * void Append(const char* aString); + * void AppendLeafValue(const input_type& aValue); + * void AppendNumber(const input_type& aValue); + * + * Data structures given may or may not have a toplevel eCSSUnit_Calc + * node representing a calc whose toplevel is not min() or max(). + */ + +template +static void +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps); + +// Serialize the toplevel value in a calc() tree. See big comment +// above. +template +static void +SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps) +{ + aOps.Append("calc("); + nsCSSUnit unit = CalcOps::GetUnit(aValue); + if (unit == eCSSUnit_Calc) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + MOZ_ASSERT(array->Count() == 1, "unexpected length"); + SerializeCalcInternal(array->Item(0), aOps); + } else { + SerializeCalcInternal(aValue, aOps); + } + aOps.Append(")"); +} + +static inline bool +IsCalcAdditiveUnit(nsCSSUnit aUnit) +{ + return aUnit == eCSSUnit_Calc_Plus || + aUnit == eCSSUnit_Calc_Minus; +} + +static inline bool +IsCalcMultiplicativeUnit(nsCSSUnit aUnit) +{ + return aUnit == eCSSUnit_Calc_Times_L || + aUnit == eCSSUnit_Calc_Times_R || + aUnit == eCSSUnit_Calc_Divided; +} + +// Serialize a non-toplevel value in a calc() tree. See big comment +// above. +template +/* static */ void +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps) +{ + nsCSSUnit unit = CalcOps::GetUnit(aValue); + if (IsCalcAdditiveUnit(unit)) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + MOZ_ASSERT(array->Count() == 2, "unexpected length"); + + SerializeCalcInternal(array->Item(0), aOps); + + if (eCSSUnit_Calc_Plus == unit) { + aOps.Append(" + "); + } else { + MOZ_ASSERT(eCSSUnit_Calc_Minus == unit, "unexpected unit"); + aOps.Append(" - "); + } + + bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1))); + if (needParens) { + aOps.Append("("); + } + SerializeCalcInternal(array->Item(1), aOps); + if (needParens) { + aOps.Append(")"); + } + } else if (IsCalcMultiplicativeUnit(unit)) { + const typename CalcOps::input_array_type *array = aValue.GetArrayValue(); + MOZ_ASSERT(array->Count() == 2, "unexpected length"); + + bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0))); + if (needParens) { + aOps.Append("("); + } + if (unit == eCSSUnit_Calc_Times_L) { + aOps.AppendNumber(array->Item(0)); + } else { + SerializeCalcInternal(array->Item(0), aOps); + } + if (needParens) { + aOps.Append(")"); + } + + if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) { + aOps.Append(" * "); + } else { + MOZ_ASSERT(eCSSUnit_Calc_Divided == unit, "unexpected unit"); + aOps.Append(" / "); + } + + nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1)); + needParens = IsCalcAdditiveUnit(subUnit) || + IsCalcMultiplicativeUnit(subUnit); + if (needParens) { + aOps.Append("("); + } + if (unit == eCSSUnit_Calc_Times_L) { + SerializeCalcInternal(array->Item(1), aOps); + } else { + aOps.AppendNumber(array->Item(1)); + } + if (needParens) { + aOps.Append(")"); + } + } else { + aOps.AppendLeafValue(aValue); + } +} + +} // namespace css + +} // namespace mozilla + +#endif /* !defined(CSSCalc_h_) */ diff --git a/layout/style/CSSEnabledState.h b/layout/style/CSSEnabledState.h new file mode 100644 index 000000000..650397072 --- /dev/null +++ b/layout/style/CSSEnabledState.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/. */ + +/* + * 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. + eForAllContent = 0, + // Enable features available in UA sheets. + eInUASheets = 0x01, + // Enable features available in chrome code. + eInChrome = 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. + eIgnoreEnabledState = 0xff +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSEnabledState) + +} // namespace mozilla + +#endif // mozilla_CSSEnabledState_h diff --git a/layout/style/CSSLexer.cpp b/layout/style/CSSLexer.cpp new file mode 100644 index 000000000..f465981e7 --- /dev/null +++ b/layout/style/CSSLexer.cpp @@ -0,0 +1,166 @@ +/* -*- 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/. */ + +#include "mozilla/dom/CSSLexer.h" +#include "js/Value.h" +#include "mozilla/dom/CSSLexerBinding.h" +#include "mozilla/dom/ToJSValue.h" + +namespace mozilla { +namespace dom { + +// Ensure that constants are consistent. + +#define CHECK(X, Y) \ + static_assert(static_cast(X) == static_cast(Y), \ + "nsCSSToken and CSSTokenType should have identical values") + +CHECK(eCSSToken_Whitespace, CSSTokenType::Whitespace); +CHECK(eCSSToken_Comment, CSSTokenType::Comment); +CHECK(eCSSToken_Ident, CSSTokenType::Ident); +CHECK(eCSSToken_Function, CSSTokenType::Function); +CHECK(eCSSToken_AtKeyword, CSSTokenType::At); +CHECK(eCSSToken_ID, CSSTokenType::Id); +CHECK(eCSSToken_Hash, CSSTokenType::Hash); +CHECK(eCSSToken_Number, CSSTokenType::Number); +CHECK(eCSSToken_Dimension, CSSTokenType::Dimension); +CHECK(eCSSToken_Percentage, CSSTokenType::Percentage); +CHECK(eCSSToken_String, CSSTokenType::String); +CHECK(eCSSToken_Bad_String, CSSTokenType::Bad_string); +CHECK(eCSSToken_URL, CSSTokenType::Url); +CHECK(eCSSToken_Bad_URL, CSSTokenType::Bad_url); +CHECK(eCSSToken_Symbol, CSSTokenType::Symbol); +CHECK(eCSSToken_Includes, CSSTokenType::Includes); +CHECK(eCSSToken_Dashmatch, CSSTokenType::Dashmatch); +CHECK(eCSSToken_Beginsmatch, CSSTokenType::Beginsmatch); +CHECK(eCSSToken_Endsmatch, CSSTokenType::Endsmatch); +CHECK(eCSSToken_Containsmatch, CSSTokenType::Containsmatch); +CHECK(eCSSToken_URange, CSSTokenType::Urange); +CHECK(eCSSToken_HTMLComment, CSSTokenType::Htmlcomment); + +#undef CHECK + +CSSLexer::CSSLexer(const nsAString& aText) + : mInput(aText) + , mScanner(mInput, 1) +{ +} + +CSSLexer::~CSSLexer() +{ +} + +bool +CSSLexer::WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle aReflector) +{ + return CSSLexerBinding::Wrap(aCx, this, aGivenProto, aReflector); +} + +uint32_t +CSSLexer::LineNumber() +{ + // The scanner uses 1-based line numbers, but our callers expect + // 0-based. + return mScanner.GetLineNumber() - 1; +} + +uint32_t +CSSLexer::ColumnNumber() +{ + return mScanner.GetColumnNumber(); +} + +void +CSSLexer::PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash, + nsAString& aResult) +{ + aResult.Append(aInputString); + uint32_t eofChars = mScanner.GetEOFCharacters(); + + if (aPreserveBackslash && + (eofChars & (nsCSSScanner::eEOFCharacters_DropBackslash | + nsCSSScanner::eEOFCharacters_ReplacementChar)) != 0) { + eofChars &= ~(nsCSSScanner::eEOFCharacters_DropBackslash | + nsCSSScanner::eEOFCharacters_ReplacementChar); + aResult.Append('\\'); + } + + if ((eofChars & nsCSSScanner::eEOFCharacters_DropBackslash) != 0 && + aResult.Length() > 0 && aResult.Last() == '\\') { + aResult.Truncate(aResult.Length() - 1); + } + + nsCSSScanner::AppendImpliedEOFCharacters(nsCSSScanner::EOFCharacters(eofChars), + aResult); +} + +void +CSSLexer::NextToken(Nullable& aResult) +{ + nsCSSToken token; + if (!mScanner.Next(token, eCSSScannerExclude_None)) { + return; + } + + CSSToken& resultToken(aResult.SetValue()); + + resultToken.mTokenType = static_cast(token.mType); + resultToken.mStartOffset = mScanner.GetTokenOffset(); + resultToken.mEndOffset = mScanner.GetTokenEndOffset(); + + switch (token.mType) { + case eCSSToken_Whitespace: + break; + + case eCSSToken_Ident: + case eCSSToken_Function: + case eCSSToken_AtKeyword: + case eCSSToken_ID: + case eCSSToken_Hash: + resultToken.mText.Construct(token.mIdent); + break; + + case eCSSToken_Dimension: + resultToken.mText.Construct(token.mIdent); + MOZ_FALLTHROUGH; + case eCSSToken_Number: + case eCSSToken_Percentage: + resultToken.mNumber.Construct(token.mNumber); + resultToken.mHasSign.Construct(token.mHasSign); + resultToken.mIsInteger.Construct(token.mIntegerValid); + break; + + case eCSSToken_String: + case eCSSToken_Bad_String: + case eCSSToken_URL: + case eCSSToken_Bad_URL: + resultToken.mText.Construct(token.mIdent); + /* Don't bother emitting the delimiter, as it is readily extracted + from the source string when needed. */ + break; + + case eCSSToken_Symbol: + resultToken.mText.Construct(nsString(&token.mSymbol, 1)); + break; + + case eCSSToken_Includes: + case eCSSToken_Dashmatch: + case eCSSToken_Beginsmatch: + case eCSSToken_Endsmatch: + case eCSSToken_Containsmatch: + case eCSSToken_URange: + break; + + case eCSSToken_Comment: + case eCSSToken_HTMLComment: + /* The comment text is easily extracted from the source string, + and is rarely useful. */ + break; + } +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/CSSLexer.h b/layout/style/CSSLexer.h new file mode 100644 index 000000000..8b41d2778 --- /dev/null +++ b/layout/style/CSSLexer.h @@ -0,0 +1,39 @@ +/* -*- 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/. */ + +#ifndef CSSLexer_h___ +#define CSSLexer_h___ + +#include "mozilla/UniquePtr.h" +#include "nsCSSScanner.h" +#include "mozilla/dom/CSSLexerBinding.h" + +namespace mozilla { +namespace dom { + +class CSSLexer : public NonRefcountedDOMObject +{ +public: + explicit CSSLexer(const nsAString&); + ~CSSLexer(); + + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, + JS::MutableHandle aReflector); + + uint32_t LineNumber(); + uint32_t ColumnNumber(); + void PerformEOFFixup(const nsAString& aInputString, bool aPreserveBackslash, + nsAString& aResult); + void NextToken(Nullable& aResult); + +private: + nsString mInput; + nsCSSScanner mScanner; +}; + +} // namespace dom +} // namespace mozilla + +#endif /* CSSLexer_h___ */ diff --git a/layout/style/CSSRuleList.cpp b/layout/style/CSSRuleList.cpp new file mode 100644 index 000000000..8493e8a37 --- /dev/null +++ b/layout/style/CSSRuleList.cpp @@ -0,0 +1,34 @@ +/* -*- 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(CSSRuleList) + NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList) + 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 CSSRuleListBinding::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 000000000..786cc1b88 --- /dev/null +++ b/layout/style/CSSRuleList.h @@ -0,0 +1,66 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_CSSRuleList_h +#define mozilla_dom_CSSRuleList_h + +#include "mozilla/StyleSheetInlines.h" +#include "nsIDOMCSSRule.h" +#include "nsIDOMCSSRuleList.h" +#include "nsWrapperCache.h" + +namespace mozilla { +namespace dom { + +// IID for the CSSRuleList interface +#define NS_ICSSRULELIST_IID \ +{ 0x56ac8d1c, 0xc1ed, 0x45fe, \ + { 0x9a, 0x4d, 0x3a, 0xdc, 0xf9, 0xd1, 0xb9, 0x3f } } + +class CSSRuleList : public nsIDOMCSSRuleList + , public nsWrapperCache +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICSSRULELIST_IID) + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CSSRuleList) + + virtual CSSStyleSheet* GetParentObject() = 0; + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override final; + + NS_IMETHOD + GetLength(uint32_t* aLength) override final + { + *aLength = Length(); + return NS_OK; + } + NS_IMETHOD + Item(uint32_t aIndex, nsIDOMCSSRule** aReturn) override final + { + NS_IF_ADDREF(*aReturn = Item(aIndex)); + return NS_OK; + } + + // WebIDL API + nsIDOMCSSRule* Item(uint32_t aIndex) + { + bool unused; + return IndexedGetter(aIndex, unused); + } + + virtual nsIDOMCSSRule* IndexedGetter(uint32_t aIndex, bool& aFound) = 0; + virtual uint32_t Length() = 0; + +protected: + virtual ~CSSRuleList() {} +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(CSSRuleList, NS_ICSSRULELIST_IID) + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_CSSRuleList_h */ diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp new file mode 100644 index 000000000..71ca6e3f2 --- /dev/null +++ b/layout/style/CSSStyleSheet.cpp @@ -0,0 +1,2026 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:tabstop=2:expandtab:shiftwidth=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/. */ + +/* representation of a CSS style sheet */ + +#include "mozilla/CSSStyleSheet.h" + +#include "nsIAtom.h" +#include "nsCSSRuleProcessor.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/MediaListBinding.h" +#include "mozilla/css/NameSpaceRule.h" +#include "mozilla/css/GroupRule.h" +#include "mozilla/css/ImportRule.h" +#include "nsCSSRules.h" +#include "nsIMediaList.h" +#include "nsIDocument.h" +#include "nsPresContext.h" +#include "nsGkAtoms.h" +#include "nsQueryObject.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsIDOMCSSStyleSheet.h" +#include "mozilla/dom/CSSRuleList.h" +#include "nsIDOMMediaList.h" +#include "nsIDOMNode.h" +#include "nsError.h" +#include "nsCSSParser.h" +#include "mozilla/css/Loader.h" +#include "nsICSSLoaderObserver.h" +#include "nsNameSpaceManager.h" +#include "nsXMLNameSpaceMap.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIScriptSecurityManager.h" +#include "mozAutoDocUpdate.h" +#include "nsRuleNode.h" +#include "nsMediaFeatures.h" +#include "nsDOMClassInfoID.h" +#include "mozilla/Likely.h" +#include "nsComponentManagerUtils.h" +#include "nsNullPrincipal.h" +#include "mozilla/RuleProcessorCache.h" +#include "nsIStyleSheetLinkingElement.h" +#include "nsDOMWindowUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// ------------------------------- +// Style Rule List for the DOM +// +class CSSRuleListImpl final : public CSSRuleList +{ +public: + explicit CSSRuleListImpl(CSSStyleSheet *aStyleSheet); + + virtual CSSStyleSheet* GetParentObject() override; + + virtual nsIDOMCSSRule* + IndexedGetter(uint32_t aIndex, bool& aFound) override; + virtual uint32_t + Length() override; + + void DropReference() { mStyleSheet = nullptr; } + +protected: + virtual ~CSSRuleListImpl(); + + CSSStyleSheet* mStyleSheet; +}; + +CSSRuleListImpl::CSSRuleListImpl(CSSStyleSheet *aStyleSheet) +{ + // Not reference counted to avoid circular references. + // The style sheet will tell us when its going away. + mStyleSheet = aStyleSheet; +} + +CSSRuleListImpl::~CSSRuleListImpl() +{ +} + +CSSStyleSheet* +CSSRuleListImpl::GetParentObject() +{ + return mStyleSheet; +} + +uint32_t +CSSRuleListImpl::Length() +{ + if (!mStyleSheet) { + return 0; + } + + return AssertedCast(mStyleSheet->StyleRuleCount()); +} + +nsIDOMCSSRule* +CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound) +{ + aFound = false; + + if (mStyleSheet) { + // ensure rules have correct parent + mStyleSheet->EnsureUniqueInner(); + css::Rule* rule = mStyleSheet->GetStyleRuleAt(aIndex); + if (rule) { + aFound = true; + return rule->GetDOMRule(); + } + } + + // Per spec: "Return Value ... null if ... not a valid index." + return nullptr; +} + +template +int32_t DoCompare(Numeric a, Numeric b) +{ + if (a == b) + return 0; + if (a < b) + return -1; + return 1; +} + +bool +nsMediaExpression::Matches(nsPresContext *aPresContext, + const nsCSSValue& aActualValue) const +{ + const nsCSSValue& actual = aActualValue; + const nsCSSValue& required = mValue; + + // If we don't have the feature, the match fails. + if (actual.GetUnit() == eCSSUnit_Null) { + return false; + } + + // If the expression had no value to match, the match succeeds, + // unless the value is an integer 0 or a zero length. + if (required.GetUnit() == eCSSUnit_Null) { + if (actual.GetUnit() == eCSSUnit_Integer) + return actual.GetIntValue() != 0; + if (actual.IsLengthUnit()) + return actual.GetFloatValue() != 0; + return true; + } + + NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxAllowed || + mRange == nsMediaExpression::eEqual, "yikes"); + int32_t cmp; // -1 (actual < required) + // 0 (actual == required) + // 1 (actual > required) + switch (mFeature->mValueType) { + case nsMediaFeature::eLength: + { + NS_ASSERTION(actual.IsLengthUnit(), "bad actual value"); + NS_ASSERTION(required.IsLengthUnit(), "bad required value"); + nscoord actualCoord = nsRuleNode::CalcLengthWithInitialFont( + aPresContext, actual); + nscoord requiredCoord = nsRuleNode::CalcLengthWithInitialFont( + aPresContext, required); + cmp = DoCompare(actualCoord, requiredCoord); + } + break; + case nsMediaFeature::eInteger: + case nsMediaFeature::eBoolInteger: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Integer, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Integer, + "bad required value"); + NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger || + actual.GetIntValue() == 0 || actual.GetIntValue() == 1, + "bad actual bool integer value"); + NS_ASSERTION(mFeature->mValueType != nsMediaFeature::eBoolInteger || + required.GetIntValue() == 0 || required.GetIntValue() == 1, + "bad required bool integer value"); + cmp = DoCompare(actual.GetIntValue(), required.GetIntValue()); + } + break; + case nsMediaFeature::eFloat: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Number, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Number, + "bad required value"); + cmp = DoCompare(actual.GetFloatValue(), required.GetFloatValue()); + } + break; + case nsMediaFeature::eIntRatio: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Array && + actual.GetArrayValue()->Count() == 2 && + actual.GetArrayValue()->Item(0).GetUnit() == + eCSSUnit_Integer && + actual.GetArrayValue()->Item(1).GetUnit() == + eCSSUnit_Integer, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Array && + required.GetArrayValue()->Count() == 2 && + required.GetArrayValue()->Item(0).GetUnit() == + eCSSUnit_Integer && + required.GetArrayValue()->Item(1).GetUnit() == + eCSSUnit_Integer, + "bad required value"); + // Convert to int64_t so we can multiply without worry. Note + // that while the spec requires that both halves of |required| + // be positive, the numerator or denominator of |actual| might + // be zero (e.g., when testing 'aspect-ratio' on a 0-width or + // 0-height iframe). + int64_t actualNum = actual.GetArrayValue()->Item(0).GetIntValue(), + actualDen = actual.GetArrayValue()->Item(1).GetIntValue(), + requiredNum = required.GetArrayValue()->Item(0).GetIntValue(), + requiredDen = required.GetArrayValue()->Item(1).GetIntValue(); + cmp = DoCompare(actualNum * requiredDen, requiredNum * actualDen); + } + break; + case nsMediaFeature::eResolution: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Inch || + actual.GetUnit() == eCSSUnit_Pixel || + actual.GetUnit() == eCSSUnit_Centimeter, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Inch || + required.GetUnit() == eCSSUnit_Pixel || + required.GetUnit() == eCSSUnit_Centimeter, + "bad required value"); + float actualDPI = actual.GetFloatValue(); + float overrideDPPX = aPresContext->GetOverrideDPPX(); + + if (overrideDPPX > 0) { + actualDPI = overrideDPPX * 96.0f; + } else if (actual.GetUnit() == eCSSUnit_Centimeter) { + actualDPI = actualDPI * 2.54f; + } else if (actual.GetUnit() == eCSSUnit_Pixel) { + actualDPI = actualDPI * 96.0f; + } + float requiredDPI = required.GetFloatValue(); + if (required.GetUnit() == eCSSUnit_Centimeter) { + requiredDPI = requiredDPI * 2.54f; + } else if (required.GetUnit() == eCSSUnit_Pixel) { + requiredDPI = requiredDPI * 96.0f; + } + cmp = DoCompare(actualDPI, requiredDPI); + } + break; + case nsMediaFeature::eEnumerated: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Enumerated, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Enumerated, + "bad required value"); + NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed, + "bad range"); // we asserted above about mRange + // We don't really need DoCompare, but it doesn't hurt (and + // maybe the compiler will condense this case with eInteger). + cmp = DoCompare(actual.GetIntValue(), required.GetIntValue()); + } + break; + case nsMediaFeature::eIdent: + { + NS_ASSERTION(actual.GetUnit() == eCSSUnit_Ident, + "bad actual value"); + NS_ASSERTION(required.GetUnit() == eCSSUnit_Ident, + "bad required value"); + NS_ASSERTION(mFeature->mRangeType == nsMediaFeature::eMinMaxNotAllowed, + "bad range"); + cmp = !(actual == required); // string comparison + } + break; + } + switch (mRange) { + case nsMediaExpression::eMin: + return cmp != -1; + case nsMediaExpression::eMax: + return cmp != 1; + case nsMediaExpression::eEqual: + return cmp == 0; + } + NS_NOTREACHED("unexpected mRange"); + return false; +} + +void +nsMediaQueryResultCacheKey::AddExpression(const nsMediaExpression* aExpression, + bool aExpressionMatches) +{ + const nsMediaFeature *feature = aExpression->mFeature; + FeatureEntry *entry = nullptr; + for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) { + if (mFeatureCache[i].mFeature == feature) { + entry = &mFeatureCache[i]; + break; + } + } + if (!entry) { + entry = mFeatureCache.AppendElement(); + if (!entry) { + return; /* out of memory */ + } + entry->mFeature = feature; + } + + ExpressionEntry eentry = { *aExpression, aExpressionMatches }; + entry->mExpressions.AppendElement(eentry); +} + +bool +nsMediaQueryResultCacheKey::Matches(nsPresContext* aPresContext) const +{ + if (aPresContext->Medium() != mMedium) { + return false; + } + + for (uint32_t i = 0; i < mFeatureCache.Length(); ++i) { + const FeatureEntry *entry = &mFeatureCache[i]; + nsCSSValue actual; + nsresult rv = + (entry->mFeature->mGetter)(aPresContext, entry->mFeature, actual); + NS_ENSURE_SUCCESS(rv, false); // any better ideas? + + for (uint32_t j = 0; j < entry->mExpressions.Length(); ++j) { + const ExpressionEntry &eentry = entry->mExpressions[j]; + if (eentry.mExpression.Matches(aPresContext, actual) != + eentry.mExpressionMatches) { + return false; + } + } + } + + return true; +} + +bool +nsDocumentRuleResultCacheKey::AddMatchingRule(css::DocumentRule* aRule) +{ + MOZ_ASSERT(!mFinalized); + return mMatchingRules.AppendElement(aRule); +} + +void +nsDocumentRuleResultCacheKey::Finalize() +{ + mMatchingRules.Sort(); +#ifdef DEBUG + mFinalized = true; +#endif +} + +#ifdef DEBUG +static bool +ArrayIsSorted(const nsTArray& aRules) +{ + for (size_t i = 1; i < aRules.Length(); i++) { + if (aRules[i - 1] > aRules[i]) { + return false; + } + } + return true; +} +#endif + +bool +nsDocumentRuleResultCacheKey::Matches( + nsPresContext* aPresContext, + const nsTArray& aRules) const +{ + MOZ_ASSERT(mFinalized); + MOZ_ASSERT(ArrayIsSorted(mMatchingRules)); + MOZ_ASSERT(ArrayIsSorted(aRules)); + +#ifdef DEBUG + for (css::DocumentRule* rule : mMatchingRules) { + MOZ_ASSERT(aRules.BinaryIndexOf(rule) != aRules.NoIndex, + "aRules must contain all rules in mMatchingRules"); + } +#endif + + // First check that aPresContext matches all the rules listed in + // mMatchingRules. + for (css::DocumentRule* rule : mMatchingRules) { + if (!rule->UseForPresentation(aPresContext)) { + return false; + } + } + + // Then check that all the rules in aRules that aren't also in + // mMatchingRules do not match. + + // pointer to matching rules + auto pm = mMatchingRules.begin(); + auto pm_end = mMatchingRules.end(); + + // pointer to all rules + auto pr = aRules.begin(); + auto pr_end = aRules.end(); + + // mMatchingRules and aRules are both sorted by their pointer values, + // so we can iterate over the two lists simultaneously. + while (pr < pr_end) { + while (pm < pm_end && *pm < *pr) { + ++pm; + } + if (pm >= pm_end || *pm != *pr) { + if ((*pr)->UseForPresentation(aPresContext)) { + return false; + } + } + ++pr; + } + return true; +} + +#ifdef DEBUG +void +nsDocumentRuleResultCacheKey::List(FILE* aOut, int32_t aIndent) const +{ + for (css::DocumentRule* rule : mMatchingRules) { + nsCString str; + + for (int32_t i = 0; i < aIndent; i++) { + str.AppendLiteral(" "); + } + str.AppendLiteral("{ "); + + nsString condition; + rule->GetConditionText(condition); + AppendUTF16toUTF8(condition, str); + + str.AppendLiteral(" }\n"); + fprintf_stderr(aOut, "%s", str.get()); + } +} +#endif + +size_t +nsDocumentRuleResultCacheKey::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + n += mMatchingRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + return n; +} + +void +nsMediaQuery::AppendToString(nsAString& aString) const +{ + if (mHadUnknownExpression) { + aString.AppendLiteral("not all"); + return; + } + + NS_ASSERTION(!mNegated || !mHasOnly, "can't have not and only"); + NS_ASSERTION(!mTypeOmitted || (!mNegated && !mHasOnly), + "can't have not or only when type is omitted"); + if (!mTypeOmitted) { + if (mNegated) { + aString.AppendLiteral("not "); + } else if (mHasOnly) { + aString.AppendLiteral("only "); + } + aString.Append(nsDependentAtomString(mMediaType)); + } + + for (uint32_t i = 0, i_end = mExpressions.Length(); i < i_end; ++i) { + if (i > 0 || !mTypeOmitted) + aString.AppendLiteral(" and "); + aString.Append('('); + + const nsMediaExpression &expr = mExpressions[i]; + const nsMediaFeature *feature = expr.mFeature; + if (feature->mReqFlags & nsMediaFeature::eHasWebkitPrefix) { + aString.AppendLiteral("-webkit-"); + } + if (expr.mRange == nsMediaExpression::eMin) { + aString.AppendLiteral("min-"); + } else if (expr.mRange == nsMediaExpression::eMax) { + aString.AppendLiteral("max-"); + } + + aString.Append(nsDependentAtomString(*feature->mName)); + + if (expr.mValue.GetUnit() != eCSSUnit_Null) { + aString.AppendLiteral(": "); + switch (feature->mValueType) { + case nsMediaFeature::eLength: + NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit"); + // Use 'width' as a property that takes length values + // written in the normal way. + expr.mValue.AppendToString(eCSSProperty_width, aString, + nsCSSValue::eNormalized); + break; + case nsMediaFeature::eInteger: + case nsMediaFeature::eBoolInteger: + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Integer, + "bad unit"); + // Use 'z-index' as a property that takes integer values + // written without anything extra. + expr.mValue.AppendToString(eCSSProperty_z_index, aString, + nsCSSValue::eNormalized); + break; + case nsMediaFeature::eFloat: + { + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Number, + "bad unit"); + // Use 'line-height' as a property that takes float values + // written in the normal way. + expr.mValue.AppendToString(eCSSProperty_line_height, aString, + nsCSSValue::eNormalized); + } + break; + case nsMediaFeature::eIntRatio: + { + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Array, + "bad unit"); + nsCSSValue::Array *array = expr.mValue.GetArrayValue(); + NS_ASSERTION(array->Count() == 2, "unexpected length"); + NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer, + "bad unit"); + NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Integer, + "bad unit"); + array->Item(0).AppendToString(eCSSProperty_z_index, aString, + nsCSSValue::eNormalized); + aString.Append('/'); + array->Item(1).AppendToString(eCSSProperty_z_index, aString, + nsCSSValue::eNormalized); + } + break; + case nsMediaFeature::eResolution: + { + aString.AppendFloat(expr.mValue.GetFloatValue()); + if (expr.mValue.GetUnit() == eCSSUnit_Inch) { + aString.AppendLiteral("dpi"); + } else if (expr.mValue.GetUnit() == eCSSUnit_Pixel) { + aString.AppendLiteral("dppx"); + } else { + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Centimeter, + "bad unit"); + aString.AppendLiteral("dpcm"); + } + } + break; + case nsMediaFeature::eEnumerated: + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Enumerated, + "bad unit"); + AppendASCIItoUTF16( + nsCSSProps::ValueToKeyword(expr.mValue.GetIntValue(), + feature->mData.mKeywordTable), + aString); + break; + case nsMediaFeature::eIdent: + NS_ASSERTION(expr.mValue.GetUnit() == eCSSUnit_Ident, + "bad unit"); + aString.Append(expr.mValue.GetStringBufferValue()); + break; + } + } + + aString.Append(')'); + } +} + +nsMediaQuery* +nsMediaQuery::Clone() const +{ + return new nsMediaQuery(*this); +} + +bool +nsMediaQuery::Matches(nsPresContext* aPresContext, + nsMediaQueryResultCacheKey* aKey) const +{ + if (mHadUnknownExpression) + return false; + + bool match = + mMediaType == aPresContext->Medium() || mMediaType == nsGkAtoms::all; + for (uint32_t i = 0, i_end = mExpressions.Length(); match && i < i_end; ++i) { + const nsMediaExpression &expr = mExpressions[i]; + nsCSSValue actual; + nsresult rv = + (expr.mFeature->mGetter)(aPresContext, expr.mFeature, actual); + NS_ENSURE_SUCCESS(rv, false); // any better ideas? + + match = expr.Matches(aPresContext, actual); + if (aKey) { + aKey->AddExpression(&expr, match); + } + } + + return match == !mNegated; +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMediaList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMediaList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMediaList) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsMediaList) + +nsMediaList::nsMediaList() + : mStyleSheet(nullptr) +{ +} + +nsMediaList::~nsMediaList() +{ +} + +/* virtual */ JSObject* +nsMediaList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return MediaListBinding::Wrap(aCx, this, aGivenProto); +} + +void +nsMediaList::GetText(nsAString& aMediaText) +{ + aMediaText.Truncate(); + + for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { + nsMediaQuery* query = mArray[i]; + + query->AppendToString(aMediaText); + + if (i + 1 < i_end) { + aMediaText.AppendLiteral(", "); + } + } +} + +// XXXbz this is so ill-defined in the spec, it's not clear quite what +// it should be doing.... +void +nsMediaList::SetText(const nsAString& aMediaText) +{ + nsCSSParser parser; + + bool htmlMode = mStyleSheet && mStyleSheet->GetOwnerNode(); + + parser.ParseMediaList(aMediaText, nullptr, 0, this, htmlMode); +} + +bool +nsMediaList::Matches(nsPresContext* aPresContext, + nsMediaQueryResultCacheKey* aKey) +{ + for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { + if (mArray[i]->Matches(aPresContext, aKey)) { + return true; + } + } + return mArray.IsEmpty(); +} + +void +nsMediaList::SetStyleSheet(CSSStyleSheet* aSheet) +{ + NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet, + "multiple style sheets competing for one media list"); + mStyleSheet = aSheet; +} + +already_AddRefed +nsMediaList::Clone() +{ + RefPtr result = new nsMediaList(); + result->mArray.AppendElements(mArray.Length()); + for (uint32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { + result->mArray[i] = mArray[i]->Clone(); + MOZ_ASSERT(result->mArray[i]); + } + return result.forget(); +} + +NS_IMETHODIMP +nsMediaList::GetMediaText(nsAString& aMediaText) +{ + GetText(aMediaText); + return NS_OK; +} + +// "sheet" should be a CSSStyleSheet and "doc" should be an +// nsCOMPtr +#define BEGIN_MEDIA_CHANGE(sheet, doc) \ + if (sheet) { \ + doc = sheet->GetOwningDocument(); \ + } \ + mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true); \ + if (sheet) { \ + sheet->WillDirty(); \ + } + +#define END_MEDIA_CHANGE(sheet, doc) \ + if (sheet) { \ + sheet->DidDirty(); \ + } \ + /* XXXldb Pass something meaningful? */ \ + if (doc) { \ + doc->StyleRuleChanged(sheet, nullptr); \ + } + + +NS_IMETHODIMP +nsMediaList::SetMediaText(const nsAString& aMediaText) +{ + nsCOMPtr doc; + + BEGIN_MEDIA_CHANGE(mStyleSheet, doc) + + SetText(aMediaText); + + END_MEDIA_CHANGE(mStyleSheet, doc) + + return NS_OK; +} + +NS_IMETHODIMP +nsMediaList::GetLength(uint32_t* aLength) +{ + NS_ENSURE_ARG_POINTER(aLength); + + *aLength = Length(); + return NS_OK; +} + +NS_IMETHODIMP +nsMediaList::Item(uint32_t aIndex, nsAString& aReturn) +{ + bool dummy; + IndexedGetter(aIndex, dummy, aReturn); + return NS_OK; +} + +void +nsMediaList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aReturn) +{ + if (aIndex < Length()) { + aFound = true; + aReturn.Truncate(); + mArray[aIndex]->AppendToString(aReturn); + } else { + aFound = false; + SetDOMStringToNull(aReturn); + } +} + +NS_IMETHODIMP +nsMediaList::DeleteMedium(const nsAString& aOldMedium) +{ + nsresult rv = NS_OK; + nsCOMPtr doc; + + BEGIN_MEDIA_CHANGE(mStyleSheet, doc) + + rv = Delete(aOldMedium); + if (NS_FAILED(rv)) + return rv; + + END_MEDIA_CHANGE(mStyleSheet, doc) + + return rv; +} + +NS_IMETHODIMP +nsMediaList::AppendMedium(const nsAString& aNewMedium) +{ + nsresult rv = NS_OK; + nsCOMPtr doc; + + BEGIN_MEDIA_CHANGE(mStyleSheet, doc) + + rv = Append(aNewMedium); + if (NS_FAILED(rv)) + return rv; + + END_MEDIA_CHANGE(mStyleSheet, doc) + + return rv; +} + +nsresult +nsMediaList::Delete(const nsAString& aOldMedium) +{ + if (aOldMedium.IsEmpty()) + return NS_ERROR_DOM_NOT_FOUND_ERR; + + for (int32_t i = 0, i_end = mArray.Length(); i < i_end; ++i) { + nsMediaQuery* query = mArray[i]; + + nsAutoString buf; + query->AppendToString(buf); + if (buf == aOldMedium) { + mArray.RemoveElementAt(i); + return NS_OK; + } + } + + return NS_ERROR_DOM_NOT_FOUND_ERR; +} + +nsresult +nsMediaList::Append(const nsAString& aNewMedium) +{ + if (aNewMedium.IsEmpty()) + return NS_ERROR_DOM_NOT_FOUND_ERR; + + Delete(aNewMedium); + + nsresult rv = NS_OK; + nsTArray > buf; + mArray.SwapElements(buf); + SetText(aNewMedium); + if (mArray.Length() == 1) { + nsMediaQuery *query = mArray[0].forget(); + if (!buf.AppendElement(query)) { + delete query; + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + + mArray.SwapElements(buf); + return rv; +} + +namespace mozilla { + +// ------------------------------- +// CSS Style Sheet Inner Data Container +// + + +CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet, + CORSMode aCORSMode, + ReferrerPolicy aReferrerPolicy, + const SRIMetadata& aIntegrity) + : StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity) +{ + MOZ_COUNT_CTOR(CSSStyleSheetInner); + mSheets.AppendElement(aPrimarySheet); +} + +static bool SetStyleSheetReference(css::Rule* aRule, void* aSheet) +{ + if (aRule) { + aRule->SetStyleSheet(static_cast(aSheet)); + } + return true; +} + +struct ChildSheetListBuilder { + RefPtr* sheetSlot; + CSSStyleSheet* parent; + + void SetParentLinks(CSSStyleSheet* aSheet) { + aSheet->mParent = parent; + aSheet->SetOwningDocument(parent->mDocument); + } + + static void ReparentChildList(CSSStyleSheet* aPrimarySheet, + CSSStyleSheet* aFirstChild) + { + for (CSSStyleSheet *child = aFirstChild; child; child = child->mNext) { + child->mParent = aPrimarySheet; + child->SetOwningDocument(aPrimarySheet->mDocument); + } + } +}; + +bool +CSSStyleSheet::RebuildChildList(css::Rule* aRule, void* aBuilder) +{ + int32_t type = aRule->GetType(); + if (type < css::Rule::IMPORT_RULE) { + // Keep going till we get to the import rules. + return true; + } + + if (type != css::Rule::IMPORT_RULE) { + // We're past all the import rules; stop the enumeration. + return false; + } + + ChildSheetListBuilder* builder = + static_cast(aBuilder); + + // XXXbz We really need to decomtaminate all this stuff. Is there a reason + // that I can't just QI to ImportRule and get a CSSStyleSheet + // directly from it? + nsCOMPtr importRule(do_QueryInterface(aRule)); + NS_ASSERTION(importRule, "GetType lied"); + + nsCOMPtr childSheet; + importRule->GetStyleSheet(getter_AddRefs(childSheet)); + + // Have to do this QI to be safe, since XPConnect can fake + // nsIDOMCSSStyleSheets + RefPtr cssSheet = do_QueryObject(childSheet); + if (!cssSheet) { + return true; + } + + (*builder->sheetSlot) = cssSheet; + builder->SetParentLinks(*builder->sheetSlot); + builder->sheetSlot = &(*builder->sheetSlot)->mNext; + return true; +} + +size_t +CSSStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + const CSSStyleSheet* s = this; + while (s) { + n += aMallocSizeOf(s); + + // Each inner can be shared by multiple sheets. So we only count the inner + // if this sheet is the last one in the list of those sharing it. As a + // result, the last such sheet takes all the blame for the memory + // consumption of the inner, which isn't ideal but it's better than + // double-counting the inner. We use last instead of first since the first + // sheet may be held in the nsXULPrototypeCache and not used in a window at + // all. + if (s->mInner->mSheets.LastElement() == s) { + n += s->mInner->SizeOfIncludingThis(aMallocSizeOf); + } + + // Measurement of the following members may be added later if DMD finds it + // is worthwhile: + // - s->mTitle + // - s->mMedia + // - s->mRuleCollection + // - s->mRuleProcessors + // + // The following members are not measured: + // - s->mOwnerRule, because it's non-owning + + s = s->mNext; + } + return n; +} + +CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy, + CSSStyleSheet* aPrimarySheet) + : StyleSheetInfo(aCopy) +{ + MOZ_COUNT_CTOR(CSSStyleSheetInner); + AddSheet(aPrimarySheet); + aCopy.mOrderedRules.EnumerateForwards(css::GroupRule::CloneRuleInto, &mOrderedRules); + mOrderedRules.EnumerateForwards(SetStyleSheetReference, aPrimarySheet); + + ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet }; + mOrderedRules.EnumerateForwards(CSSStyleSheet::RebuildChildList, &builder); + + RebuildNameSpaces(); +} + +CSSStyleSheetInner::~CSSStyleSheetInner() +{ + MOZ_COUNT_DTOR(CSSStyleSheetInner); + mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr); +} + +CSSStyleSheetInner* +CSSStyleSheetInner::CloneFor(CSSStyleSheet* aPrimarySheet) +{ + return new CSSStyleSheetInner(*this, aPrimarySheet); +} + +void +CSSStyleSheetInner::AddSheet(CSSStyleSheet* aSheet) +{ + mSheets.AppendElement(aSheet); +} + +void +CSSStyleSheetInner::RemoveSheet(CSSStyleSheet* aSheet) +{ + if (1 == mSheets.Length()) { + NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent"); + delete this; + return; + } + if (aSheet == mSheets.ElementAt(0)) { + mSheets.RemoveElementAt(0); + NS_ASSERTION(mSheets.Length(), "no parents"); + mOrderedRules.EnumerateForwards(SetStyleSheetReference, + mSheets.ElementAt(0)); + + ChildSheetListBuilder::ReparentChildList(mSheets[0], mFirstChild); + } + else { + mSheets.RemoveElement(aSheet); + } +} + +static void +AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap) +{ + NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type"); + + RefPtr nameSpaceRule = do_QueryObject(aRule); + + nsAutoString urlSpec; + nameSpaceRule->GetURLSpec(urlSpec); + + aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec); +} + +static bool +CreateNameSpace(css::Rule* aRule, void* aNameSpacePtr) +{ + int32_t type = aRule->GetType(); + if (css::Rule::NAMESPACE_RULE == type) { + AddNamespaceRuleToMap(aRule, + static_cast(aNameSpacePtr)); + return true; + } + // stop if not namespace, import or charset because namespace can't follow + // anything else + return (css::Rule::CHARSET_RULE == type || css::Rule::IMPORT_RULE == type); +} + +void +CSSStyleSheetInner::RebuildNameSpaces() +{ + // Just nuke our existing namespace map, if any + if (NS_SUCCEEDED(CreateNamespaceMap())) { + mOrderedRules.EnumerateForwards(CreateNameSpace, mNameSpaceMap); + } +} + +nsresult +CSSStyleSheetInner::CreateNamespaceMap() +{ + mNameSpaceMap = nsXMLNameSpaceMap::Create(false); + NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY); + // Override the default namespace map behavior for the null prefix to + // return the wildcard namespace instead of the null namespace. + mNameSpaceMap->AddPrefix(nullptr, kNameSpaceID_Unknown); + return NS_OK; +} + +size_t +CSSStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mOrderedRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (size_t i = 0; i < mOrderedRules.Length(); i++) { + n += mOrderedRules[i]->SizeOfIncludingThis(aMallocSizeOf); + } + n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0; + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mSheetURI + // - mOriginalSheetURI + // - mBaseURI + // - mPrincipal + // - mNameSpaceMap + // + // The following members are not measured: + // - mSheets, because it's non-owning + + return n; +} + +// ------------------------------- +// CSS Style Sheet +// + +CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode, + CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy) + : StyleSheet(StyleBackendType::Gecko, aParsingMode), + mParent(nullptr), + mOwnerRule(nullptr), + mDirty(false), + mInRuleProcessorCache(false), + mScopeElement(nullptr), + mRuleProcessors(nullptr) +{ + mInner = new CSSStyleSheetInner(this, aCORSMode, aReferrerPolicy, + SRIMetadata()); +} + +CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode, + CORSMode aCORSMode, + ReferrerPolicy aReferrerPolicy, + const SRIMetadata& aIntegrity) + : StyleSheet(StyleBackendType::Gecko, aParsingMode), + mParent(nullptr), + mOwnerRule(nullptr), + mDirty(false), + mInRuleProcessorCache(false), + mScopeElement(nullptr), + mRuleProcessors(nullptr) +{ + mInner = new CSSStyleSheetInner(this, aCORSMode, aReferrerPolicy, + aIntegrity); +} + +CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy, + CSSStyleSheet* aParentToUse, + css::ImportRule* aOwnerRuleToUse, + nsIDocument* aDocumentToUse, + nsINode* aOwningNodeToUse) + : StyleSheet(aCopy, aDocumentToUse, aOwningNodeToUse), + mParent(aParentToUse), + mOwnerRule(aOwnerRuleToUse), + mDirty(aCopy.mDirty), + mInRuleProcessorCache(false), + mScopeElement(nullptr), + mInner(aCopy.mInner), + mRuleProcessors(nullptr) +{ + + mInner->AddSheet(this); + + if (mDirty) { // CSSOM's been there, force full copy now + NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?"); + // FIXME: handle failure? + EnsureUniqueInner(); + } + + if (aCopy.mMedia) { + // XXX This is wrong; we should be keeping @import rules and + // sheets in sync! + mMedia = aCopy.mMedia->Clone(); + } +} + +CSSStyleSheet::~CSSStyleSheet() +{ + for (CSSStyleSheet* child = mInner->mFirstChild; + child; + child = child->mNext) { + // XXXbz this is a little bogus; see the XXX comment where we + // declare mFirstChild. + if (child->mParent == this) { + child->mParent = nullptr; + child->mDocument = nullptr; + } + } + DropRuleCollection(); + DropMedia(); + mInner->RemoveSheet(this); + // XXX The document reference is not reference counted and should + // not be released. The document will let us know when it is going + // away. + if (mRuleProcessors) { + NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference"); + delete mRuleProcessors; // weak refs, should be empty here anyway + } + if (mInRuleProcessorCache) { + RuleProcessorCache::RemoveSheet(this); + } +} + +void +CSSStyleSheet::DropRuleCollection() +{ + if (mRuleCollection) { + mRuleCollection->DropReference(); + mRuleCollection = nullptr; + } +} + +void +CSSStyleSheet::DropMedia() +{ + if (mMedia) { + mMedia->SetStyleSheet(nullptr); + mMedia = nullptr; + } +} + +void +CSSStyleSheet::UnlinkInner() +{ + // We can only have a cycle through our inner if we have a unique inner, + // because otherwise there are no JS wrappers for anything in the inner. + if (mInner->mSheets.Length() != 1) { + return; + } + + mInner->mOrderedRules.EnumerateForwards(SetStyleSheetReference, nullptr); + mInner->mOrderedRules.Clear(); + + // Have to be a bit careful with child sheets, because we want to + // drop their mNext pointers and null out their mParent and + // mDocument, but don't want to work with deleted objects. And we + // don't want to do any addrefing in the process, just to make sure + // we don't confuse the cycle collector (though on the face of it, + // addref/release pairs during unlink should probably be ok). + RefPtr child; + child.swap(mInner->mFirstChild); + while (child) { + MOZ_ASSERT(child->mParent == this, "We have a unique inner!"); + child->mParent = nullptr; + child->mDocument = nullptr; + RefPtr next; + // Null out child->mNext, but don't let it die yet + next.swap(child->mNext); + // Switch to looking at the old value of child->mNext next iteration + child.swap(next); + // "next" is now our previous value of child; it'll get released + // as we loop around. + } +} + +void +CSSStyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb) +{ + // We can only have a cycle through our inner if we have a unique inner, + // because otherwise there are no JS wrappers for anything in the inner. + if (mInner->mSheets.Length() != 1) { + return; + } + + RefPtr* childSheetSlot = &mInner->mFirstChild; + while (*childSheetSlot) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet"); + cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, childSheetSlot->get())); + childSheetSlot = &(*childSheetSlot)->mNext; + } + + const nsCOMArray& rules = mInner->mOrderedRules; + for (int32_t i = 0, count = rules.Count(); i < count; ++i) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]"); + cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule()); + } +} + +// QueryInterface implementation for CSSStyleSheet +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CSSStyleSheet) + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, StyleSheet) + if (aIID.Equals(NS_GET_IID(CSSStyleSheet))) + foundInterface = reinterpret_cast(this); + else +NS_INTERFACE_MAP_END_INHERITING(StyleSheet) + +NS_IMPL_ADDREF_INHERITED(CSSStyleSheet, StyleSheet) +NS_IMPL_RELEASE_INHERITED(CSSStyleSheet, StyleSheet) + +NS_IMPL_CYCLE_COLLECTION_CLASS(CSSStyleSheet) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSStyleSheet) + tmp->DropMedia(); + // We do not unlink mNext; our parent will handle that. If we + // unlinked it here, our parent would not be able to walk its list + // of child sheets and null out the back-references to it, if we got + // unlinked before it does. + tmp->DropRuleCollection(); + tmp->UnlinkInner(); + tmp->mScopeElement = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(StyleSheet) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSStyleSheet, StyleSheet) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia) + // We do not traverse mNext; our parent will handle that. See + // comments in Unlink for why. + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement) + tmp->TraverseInner(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +nsresult +CSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor) +{ + if (! mRuleProcessors) { + mRuleProcessors = new AutoTArray(); + if (!mRuleProcessors) + return NS_ERROR_OUT_OF_MEMORY; + } + NS_ASSERTION(mRuleProcessors->NoIndex == mRuleProcessors->IndexOf(aProcessor), + "processor already registered"); + mRuleProcessors->AppendElement(aProcessor); // weak ref + return NS_OK; +} + +nsresult +CSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor) +{ + if (!mRuleProcessors) + return NS_ERROR_FAILURE; + return mRuleProcessors->RemoveElement(aProcessor) + ? NS_OK + : NS_ERROR_FAILURE; +} + +void +CSSStyleSheet::AddStyleSet(nsStyleSet* aStyleSet) +{ + NS_ASSERTION(!mStyleSets.Contains(aStyleSet), + "style set already registered"); + mStyleSets.AppendElement(aStyleSet); +} + +void +CSSStyleSheet::DropStyleSet(nsStyleSet* aStyleSet) +{ + DebugOnly found = mStyleSets.RemoveElement(aStyleSet); + NS_ASSERTION(found, "didn't find style set"); +} + +bool +CSSStyleSheet::UseForPresentation(nsPresContext* aPresContext, + nsMediaQueryResultCacheKey& aKey) const +{ + if (mMedia) { + return mMedia->Matches(aPresContext, &aKey); + } + return true; +} + + +void +CSSStyleSheet::SetMedia(nsMediaList* aMedia) +{ + mMedia = aMedia; +} + +bool +CSSStyleSheet::HasRules() const +{ + return StyleRuleCount() != 0; +} + +void +CSSStyleSheet::SetEnabled(bool aEnabled) +{ + // Internal method, so callers must handle BeginUpdate/EndUpdate + bool oldDisabled = mDisabled; + mDisabled = !aEnabled; + + if (mInner->mComplete && oldDisabled != mDisabled) { + ClearRuleCascades(); + + if (mDocument) { + mDocument->SetStyleSheetApplicableState(this, !mDisabled); + } + } +} + +CSSStyleSheet* +CSSStyleSheet::GetParentSheet() const +{ + return mParent; +} + +void +CSSStyleSheet::SetOwningDocument(nsIDocument* aDocument) +{ // not ref counted + mDocument = aDocument; + // Now set the same document on all our child sheets.... + // XXXbz this is a little bogus; see the XXX comment where we + // declare mFirstChild. + for (CSSStyleSheet* child = mInner->mFirstChild; + child; child = child->mNext) { + if (child->mParent == this) { + child->SetOwningDocument(aDocument); + } + } +} + +uint64_t +CSSStyleSheet::FindOwningWindowInnerID() const +{ + uint64_t windowID = 0; + if (mDocument) { + windowID = mDocument->InnerWindowID(); + } + + if (windowID == 0 && mOwningNode) { + windowID = mOwningNode->OwnerDoc()->InnerWindowID(); + } + + if (windowID == 0 && mOwnerRule) { + RefPtr sheet = static_cast(mOwnerRule)->GetStyleSheet(); + if (sheet) { + windowID = sheet->FindOwningWindowInnerID(); + } + } + + if (windowID == 0 && mParent) { + windowID = mParent->FindOwningWindowInnerID(); + } + + return windowID; +} + +void +CSSStyleSheet::AppendStyleSheet(CSSStyleSheet* aSheet) +{ + NS_PRECONDITION(nullptr != aSheet, "null arg"); + + WillDirty(); + RefPtr* tail = &mInner->mFirstChild; + while (*tail) { + tail = &(*tail)->mNext; + } + *tail = aSheet; + + // This is not reference counted. Our parent tells us when + // it's going away. + aSheet->mParent = this; + aSheet->mDocument = mDocument; + DidDirty(); +} + +void +CSSStyleSheet::AppendStyleRule(css::Rule* aRule) +{ + NS_PRECONDITION(nullptr != aRule, "null arg"); + + WillDirty(); + mInner->mOrderedRules.AppendObject(aRule); + aRule->SetStyleSheet(this); + DidDirty(); + + if (css::Rule::NAMESPACE_RULE == aRule->GetType()) { +#ifdef DEBUG + nsresult rv = +#endif + RegisterNamespaceRule(aRule); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "RegisterNamespaceRule returned error"); + } +} + +int32_t +CSSStyleSheet::StyleRuleCount() const +{ + return mInner->mOrderedRules.Count(); +} + +css::Rule* +CSSStyleSheet::GetStyleRuleAt(int32_t aIndex) const +{ + // Important: If this function is ever made scriptable, we must add + // a security check here. See GetCssRules below for an example. + return mInner->mOrderedRules.SafeObjectAt(aIndex); +} + +void +CSSStyleSheet::EnsureUniqueInner() +{ + mDirty = true; + + MOZ_ASSERT(mInner->mSheets.Length() != 0, + "unexpected number of outers"); + if (mInner->mSheets.Length() == 1) { + // already unique + return; + } + CSSStyleSheetInner* clone = mInner->CloneFor(this); + MOZ_ASSERT(clone); + mInner->RemoveSheet(this); + mInner = clone; + + // otherwise the rule processor has pointers to the old rules + ClearRuleCascades(); + + // let our containing style sets know that if we call + // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the + // document + for (nsStyleSet* styleSet : mStyleSets) { + styleSet->SetNeedsRestyleAfterEnsureUniqueInner(); + } +} + +void +CSSStyleSheet::AppendAllChildSheets(nsTArray& aArray) +{ + for (CSSStyleSheet* child = mInner->mFirstChild; child; + child = child->mNext) { + aArray.AppendElement(child); + } +} + +already_AddRefed +CSSStyleSheet::Clone(CSSStyleSheet* aCloneParent, + css::ImportRule* aCloneOwnerRule, + nsIDocument* aCloneDocument, + nsINode* aCloneOwningNode) const +{ + RefPtr clone = new CSSStyleSheet(*this, + aCloneParent, + aCloneOwnerRule, + aCloneDocument, + aCloneOwningNode); + return clone.forget(); +} + +#ifdef DEBUG +static void +ListRules(const nsCOMArray& aRules, FILE* aOut, int32_t aIndent) +{ + for (int32_t index = aRules.Count() - 1; index >= 0; --index) { + aRules.ObjectAt(index)->List(aOut, aIndent); + } +} + +struct ListEnumData { + ListEnumData(FILE* aOut, int32_t aIndent) + : mOut(aOut), + mIndent(aIndent) + { + } + FILE* mOut; + int32_t mIndent; +}; + +void +CSSStyleSheet::List(FILE* out, int32_t aIndent) const +{ + + int32_t index; + + // Indent + nsAutoCString str; + for (index = aIndent; --index >= 0; ) { + str.AppendLiteral(" "); + } + + str.AppendLiteral("CSS Style Sheet: "); + nsAutoCString urlSpec; + nsresult rv = mInner->mSheetURI->GetSpec(urlSpec); + if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) { + str.Append(urlSpec); + } + + if (mMedia) { + str.AppendLiteral(" media: "); + nsAutoString buffer; + mMedia->GetText(buffer); + AppendUTF16toUTF8(buffer, str); + } + str.Append('\n'); + fprintf_stderr(out, "%s", str.get()); + + for (const CSSStyleSheet* child = mInner->mFirstChild; + child; + child = child->mNext) { + child->List(out, aIndent + 1); + } + + fprintf_stderr(out, "%s", "Rules in source order:\n"); + ListRules(mInner->mOrderedRules, out, aIndent); +} +#endif + +void +CSSStyleSheet::ClearRuleCascades() +{ + // We might be in ClearRuleCascades because we had a modification + // to the sheet that resulted in an nsCSSSelector being destroyed. + // Tell the RestyleManager for each document we're used in + // so that they can drop any nsCSSSelector pointers (used for + // eRestyle_SomeDescendants) in their mPendingRestyles. + for (nsStyleSet* styleSet : mStyleSets) { + styleSet->ClearSelectors(); + } + + bool removedSheetFromRuleProcessorCache = false; + if (mRuleProcessors) { + nsCSSRuleProcessor **iter = mRuleProcessors->Elements(), + **end = iter + mRuleProcessors->Length(); + for(; iter != end; ++iter) { + if (!removedSheetFromRuleProcessorCache && (*iter)->IsShared()) { + // Since the sheet has been modified, we need to remove all + // RuleProcessorCache entries that contain this sheet, as the + // list of @-moz-document rules might have changed. + RuleProcessorCache::RemoveSheet(this); + removedSheetFromRuleProcessorCache = true; + } + (*iter)->ClearRuleCascades(); + } + } + if (mParent) { + CSSStyleSheet* parent = (CSSStyleSheet*)mParent; + parent->ClearRuleCascades(); + } +} + +void +CSSStyleSheet::WillDirty() +{ + if (mInner->mComplete) { + EnsureUniqueInner(); + } +} + +void +CSSStyleSheet::DidDirty() +{ + MOZ_ASSERT(!mInner->mComplete || mDirty, + "caller must have called WillDirty()"); + ClearRuleCascades(); +} + +nsresult +CSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule) +{ + if (!mInner->mNameSpaceMap) { + nsresult rv = mInner->CreateNamespaceMap(); + NS_ENSURE_SUCCESS(rv, rv); + } + + AddNamespaceRuleToMap(aRule, mInner->mNameSpaceMap); + return NS_OK; +} + +nsMediaList* +CSSStyleSheet::Media() +{ + if (!mMedia) { + mMedia = new nsMediaList(); + mMedia->SetStyleSheet(this); + } + + return mMedia; +} + +nsIDOMCSSRule* +CSSStyleSheet::GetDOMOwnerRule() const +{ + return mOwnerRule ? mOwnerRule->GetDOMRule() : nullptr; +} + +CSSRuleList* +CSSStyleSheet::GetCssRulesInternal(ErrorResult& aRv) +{ + if (!mRuleCollection) { + mRuleCollection = new CSSRuleListImpl(this); + } + return mRuleCollection; +} + +static bool +RuleHasPendingChildSheet(css::Rule *cssRule) +{ + nsCOMPtr importRule(do_QueryInterface(cssRule)); + NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!"); + nsCOMPtr childSheet; + importRule->GetStyleSheet(getter_AddRefs(childSheet)); + RefPtr cssSheet = do_QueryObject(childSheet); + return cssSheet != nullptr && !cssSheet->IsComplete(); +} + +uint32_t +CSSStyleSheet::InsertRuleInternal(const nsAString& aRule, + uint32_t aIndex, + ErrorResult& aRv) +{ + MOZ_ASSERT(mInner->mComplete); + + WillDirty(); + + if (aIndex > uint32_t(mInner->mOrderedRules.Count())) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return 0; + } + + NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX, + "Too many style rules!"); + + // Hold strong ref to the CSSLoader in case the document update + // kills the document + RefPtr loader; + if (mDocument) { + loader = mDocument->CSSLoader(); + NS_ASSERTION(loader, "Document with no CSS loader!"); + } + + nsCSSParser css(loader, this); + + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + RefPtr rule; + aRv = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, + mInner->mPrincipal, getter_AddRefs(rule)); + if (NS_WARN_IF(aRv.Failed())) { + return 0; + } + + // Hierarchy checking. + int32_t newType = rule->GetType(); + + // check that we're not inserting before a charset rule + css::Rule* nextRule = mInner->mOrderedRules.SafeObjectAt(aIndex); + if (nextRule) { + int32_t nextType = nextRule->GetType(); + if (nextType == css::Rule::CHARSET_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + + if (nextType == css::Rule::IMPORT_RULE && + newType != css::Rule::CHARSET_RULE && + newType != css::Rule::IMPORT_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + + if (nextType == css::Rule::NAMESPACE_RULE && + newType != css::Rule::CHARSET_RULE && + newType != css::Rule::IMPORT_RULE && + newType != css::Rule::NAMESPACE_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + } + + if (aIndex != 0) { + // no inserting charset at nonzero position + if (newType == css::Rule::CHARSET_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + + css::Rule* prevRule = mInner->mOrderedRules.SafeObjectAt(aIndex - 1); + int32_t prevType = prevRule->GetType(); + + if (newType == css::Rule::IMPORT_RULE && + prevType != css::Rule::CHARSET_RULE && + prevType != css::Rule::IMPORT_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + + if (newType == css::Rule::NAMESPACE_RULE && + prevType != css::Rule::CHARSET_RULE && + prevType != css::Rule::IMPORT_RULE && + prevType != css::Rule::NAMESPACE_RULE) { + aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); + return 0; + } + } + + if (!mInner->mOrderedRules.InsertObjectAt(rule, aIndex)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return 0; + } + + DidDirty(); + + rule->SetStyleSheet(this); + + int32_t type = rule->GetType(); + if (type == css::Rule::NAMESPACE_RULE) { + // XXXbz does this screw up when inserting a namespace rule before + // another namespace rule that binds the same prefix to a different + // namespace? + aRv = RegisterNamespaceRule(rule); + if (NS_WARN_IF(aRv.Failed())) { + return 0; + } + } + + // We don't notify immediately for @import rules, but rather when + // the sheet the rule is importing is loaded (see StyleSheetLoaded) + if ((type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) && + mDocument) { + mDocument->StyleRuleAdded(this, rule); + } + + return aIndex; +} + +void +CSSStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv) +{ + // XXX TBI: handle @rule types + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + WillDirty(); + + if (aIndex >= uint32_t(mInner->mOrderedRules.Count())) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + NS_ASSERTION(uint32_t(mInner->mOrderedRules.Count()) <= INT32_MAX, + "Too many style rules!"); + + // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt + RefPtr rule = mInner->mOrderedRules.ObjectAt(aIndex); + if (rule) { + mInner->mOrderedRules.RemoveObjectAt(aIndex); + if (mDocument && mDocument->StyleSheetChangeEventsEnabled()) { + // Force creation of the DOM rule, so that it can be put on the + // StyleRuleRemoved event object. + rule->GetDOMRule(); + } + rule->SetStyleSheet(nullptr); + DidDirty(); + + if (mDocument) { + mDocument->StyleRuleRemoved(this, rule); + } + } +} + +nsresult +CSSStyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex) +{ + NS_ENSURE_ARG_POINTER(aGroup); + NS_ASSERTION(mInner->mComplete, "No deleting from an incomplete sheet!"); + RefPtr rule = aGroup->GetStyleRuleAt(aIndex); + NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE); + + // check that the rule actually belongs to this sheet! + if (this != rule->GetStyleSheet()) { + return NS_ERROR_INVALID_ARG; + } + + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + WillDirty(); + + nsresult result = aGroup->DeleteStyleRuleAt(aIndex); + NS_ENSURE_SUCCESS(result, result); + + rule->SetStyleSheet(nullptr); + + DidDirty(); + + if (mDocument) { + mDocument->StyleRuleRemoved(this, rule); + } + + return NS_OK; +} + +nsresult +CSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule, + css::GroupRule* aGroup, + uint32_t aIndex, + uint32_t* _retval) +{ + NS_ASSERTION(mInner->mComplete, "No inserting into an incomplete sheet!"); + // check that the group actually belongs to this sheet! + if (this != aGroup->GetStyleSheet()) { + return NS_ERROR_INVALID_ARG; + } + + // Hold strong ref to the CSSLoader in case the document update + // kills the document + RefPtr loader; + if (mDocument) { + loader = mDocument->CSSLoader(); + NS_ASSERTION(loader, "Document with no CSS loader!"); + } + + nsCSSParser css(loader, this); + + // parse and grab the rule + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + WillDirty(); + + RefPtr rule; + nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI, + mInner->mPrincipal, getter_AddRefs(rule)); + if (NS_FAILED(result)) + return result; + + switch (rule->GetType()) { + case css::Rule::STYLE_RULE: + case css::Rule::MEDIA_RULE: + case css::Rule::FONT_FACE_RULE: + case css::Rule::PAGE_RULE: + case css::Rule::KEYFRAMES_RULE: + case css::Rule::COUNTER_STYLE_RULE: + case css::Rule::DOCUMENT_RULE: + case css::Rule::SUPPORTS_RULE: + // these types are OK to insert into a group + break; + case css::Rule::CHARSET_RULE: + case css::Rule::IMPORT_RULE: + case css::Rule::NAMESPACE_RULE: + // these aren't + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + default: + NS_NOTREACHED("unexpected rule type"); + return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; + } + + result = aGroup->InsertStyleRuleAt(aIndex, rule); + NS_ENSURE_SUCCESS(result, result); + DidDirty(); + + if (mDocument) { + mDocument->StyleRuleAdded(this, rule); + } + + *_retval = aIndex; + return NS_OK; +} + +// nsICSSLoaderObserver implementation +NS_IMETHODIMP +CSSStyleSheet::StyleSheetLoaded(StyleSheet* aSheet, + bool aWasAlternate, + nsresult aStatus) +{ + MOZ_ASSERT(aSheet->IsGecko(), + "why we were called back with a ServoStyleSheet?"); + + CSSStyleSheet* sheet = aSheet->AsGecko(); + + if (sheet->GetParentSheet() == nullptr) { + return NS_OK; // ignore if sheet has been detached already (see parseSheet) + } + NS_ASSERTION(this == sheet->GetParentSheet(), + "We are being notified of a sheet load for a sheet that is not our child!"); + + if (mDocument && NS_SUCCEEDED(aStatus)) { + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + // XXXldb @import rules shouldn't even implement nsIStyleRule (but + // they do)! + mDocument->StyleRuleAdded(this, sheet->GetOwnerRule()); + } + + return NS_OK; +} + +nsresult +CSSStyleSheet::ReparseSheet(const nsAString& aInput) +{ + // Not doing this if the sheet is not complete! + if (!mInner->mComplete) { + return NS_ERROR_DOM_INVALID_ACCESS_ERR; + } + + // Hold strong ref to the CSSLoader in case the document update + // kills the document + RefPtr loader; + if (mDocument) { + loader = mDocument->CSSLoader(); + NS_ASSERTION(loader, "Document with no CSS loader!"); + } else { + loader = new css::Loader(StyleBackendType::Gecko); + } + + mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true); + + WillDirty(); + + // detach existing rules (including child sheets via import rules) + css::LoaderReusableStyleSheets reusableSheets; + int ruleCount; + while ((ruleCount = mInner->mOrderedRules.Count()) != 0) { + RefPtr rule = mInner->mOrderedRules.ObjectAt(ruleCount - 1); + mInner->mOrderedRules.RemoveObjectAt(ruleCount - 1); + rule->SetStyleSheet(nullptr); + if (rule->GetType() == css::Rule::IMPORT_RULE) { + nsCOMPtr importRule(do_QueryInterface(rule)); + NS_ASSERTION(importRule, "GetType lied"); + + nsCOMPtr childSheet; + importRule->GetStyleSheet(getter_AddRefs(childSheet)); + + RefPtr cssSheet = do_QueryObject(childSheet); + if (cssSheet && cssSheet->GetOriginalURI()) { + reusableSheets.AddReusableSheet(cssSheet); + } + } + if (mDocument) { + mDocument->StyleRuleRemoved(this, rule); + } + } + + // nuke child sheets list and current namespace map + for (CSSStyleSheet* child = mInner->mFirstChild; child; ) { + NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!"); + CSSStyleSheet* next = child->mNext; + child->mParent = nullptr; + child->mDocument = nullptr; + child->mNext = nullptr; + child = next; + } + mInner->mFirstChild = nullptr; + mInner->mNameSpaceMap = nullptr; + + uint32_t lineNumber = 1; + if (mOwningNode) { + nsCOMPtr link = do_QueryInterface(mOwningNode); + if (link) { + lineNumber = link->GetLineNumber(); + } + } + + nsCSSParser parser(loader, this); + nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI, + mInner->mPrincipal, lineNumber, &reusableSheets); + DidDirty(); // we are always 'dirty' here since we always remove rules first + NS_ENSURE_SUCCESS(rv, rv); + + // notify document of all new rules + if (mDocument) { + for (int32_t index = 0; index < mInner->mOrderedRules.Count(); ++index) { + RefPtr rule = mInner->mOrderedRules.ObjectAt(index); + if (rule->GetType() == css::Rule::IMPORT_RULE && + RuleHasPendingChildSheet(rule)) { + continue; // notify when loaded (see StyleSheetLoaded) + } + mDocument->StyleRuleAdded(this, rule); + } + } + return NS_OK; +} + +} // namespace mozilla diff --git a/layout/style/CSSStyleSheet.h b/layout/style/CSSStyleSheet.h new file mode 100644 index 000000000..74e12291e --- /dev/null +++ b/layout/style/CSSStyleSheet.h @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:tabstop=2:expandtab:shiftwidth=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/. */ + +/* representation of a CSS style sheet */ + +#ifndef mozilla_CSSStyleSheet_h +#define mozilla_CSSStyleSheet_h + +#include "mozilla/Attributes.h" +#include "mozilla/IncrementalClearCOMRuleArray.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInfo.h" +#include "mozilla/css/SheetParsingMode.h" +#include "mozilla/dom/Element.h" + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsICSSLoaderObserver.h" +#include "nsTArrayForwardDeclare.h" +#include "nsString.h" +#include "mozilla/CORSMode.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/net/ReferrerPolicy.h" +#include "mozilla/dom/SRIMetadata.h" + +class CSSRuleListImpl; +class nsCSSRuleProcessor; +class nsIURI; +class nsMediaList; +class nsMediaQueryResultCacheKey; +class nsStyleSet; +class nsPresContext; +class nsXMLNameSpaceMap; + +namespace mozilla { +struct ChildSheetListBuilder; +class CSSStyleSheet; + +namespace css { +class Rule; +class GroupRule; +class ImportRule; +} // namespace css +namespace dom { +class CSSRuleList; +} // namespace dom + + // ------------------------------- +// CSS Style Sheet Inner Data Container +// + +struct CSSStyleSheetInner : public StyleSheetInfo +{ + CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet, + CORSMode aCORSMode, + ReferrerPolicy aReferrerPolicy, + const dom::SRIMetadata& aIntegrity); + CSSStyleSheetInner(CSSStyleSheetInner& aCopy, + CSSStyleSheet* aPrimarySheet); + ~CSSStyleSheetInner(); + + CSSStyleSheetInner* CloneFor(CSSStyleSheet* aPrimarySheet); + void AddSheet(CSSStyleSheet* aSheet); + void RemoveSheet(CSSStyleSheet* aSheet); + + void RebuildNameSpaces(); + + // Create a new namespace map + nsresult CreateNamespaceMap(); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + AutoTArray mSheets; + IncrementalClearCOMRuleArray mOrderedRules; + nsAutoPtr mNameSpaceMap; + // Linked list of child sheets. This is al fundamentally broken, because + // each of the child sheets has a unique parent... We can only hope (and + // currently this is the case) that any time page JS can get ts hands on a + // child sheet that means we've already ensured unique inners throughout its + // parent chain and things are good. + RefPtr mFirstChild; +}; + + +// ------------------------------- +// CSS Style Sheet +// + +// CID for the CSSStyleSheet class +// 7985c7ac-9ddc-444d-9899-0c86ec122f54 +#define NS_CSS_STYLE_SHEET_IMPL_CID \ +{ 0x7985c7ac, 0x9ddc, 0x444d, \ + { 0x98, 0x99, 0x0c, 0x86, 0xec, 0x12, 0x2f, 0x54 } } + + +class CSSStyleSheet final : public StyleSheet + , public nsICSSLoaderObserver +{ +public: + typedef net::ReferrerPolicy ReferrerPolicy; + CSSStyleSheet(css::SheetParsingMode aParsingMode, + CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy); + CSSStyleSheet(css::SheetParsingMode aParsingMode, + CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy, + const dom::SRIMetadata& aIntegrity); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CSSStyleSheet, StyleSheet) + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_SHEET_IMPL_CID) + + bool HasRules() const; + + /** + * Set the stylesheet to be enabled. This may or may not make it + * applicable. Note that this WILL inform the sheet's document of + * its new applicable state if the state changes but WILL NOT call + * BeginUpdate() or EndUpdate() on the document -- calling those is + * the caller's responsibility. This allows use of SetEnabled when + * batched updates are desired. If you want updates handled for + * you, see nsIDOMStyleSheet::SetDisabled(). + */ + void SetEnabled(bool aEnabled); + + // style sheet owner info + CSSStyleSheet* GetParentSheet() const; // may be null + void SetOwningDocument(nsIDocument* aDocument); + + // Find the ID of the owner inner window. + uint64_t FindOwningWindowInnerID() const; +#ifdef DEBUG + void List(FILE* out = stdout, int32_t aIndent = 0) const; +#endif + + void AppendStyleSheet(CSSStyleSheet* aSheet); + + // XXX do these belong here or are they generic? + void AppendStyleRule(css::Rule* aRule); + + int32_t StyleRuleCount() const; + css::Rule* GetStyleRuleAt(int32_t aIndex) const; + + nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex); + nsresult InsertRuleIntoGroup(const nsAString& aRule, css::GroupRule* aGroup, uint32_t aIndex, uint32_t* _retval); + + void SetTitle(const nsAString& aTitle) { mTitle = aTitle; } + void SetMedia(nsMediaList* aMedia); + + void SetOwnerRule(css::ImportRule* aOwnerRule) { mOwnerRule = aOwnerRule; /* Not ref counted */ } + css::ImportRule* GetOwnerRule() const { return mOwnerRule; } + // Workaround overloaded-virtual warning in GCC. + using StyleSheet::GetOwnerRule; + + nsXMLNameSpaceMap* GetNameSpaceMap() const { return mInner->mNameSpaceMap; } + + already_AddRefed Clone(CSSStyleSheet* aCloneParent, + css::ImportRule* aCloneOwnerRule, + nsIDocument* aCloneDocument, + nsINode* aCloneOwningNode) const; + + bool IsModified() const { return mDirty; } + + void SetModifiedByChildRule() { + NS_ASSERTION(mDirty, + "sheet must be marked dirty before handing out child rules"); + DidDirty(); + } + + nsresult AddRuleProcessor(nsCSSRuleProcessor* aProcessor); + nsresult DropRuleProcessor(nsCSSRuleProcessor* aProcessor); + + void AddStyleSet(nsStyleSet* aStyleSet); + void DropStyleSet(nsStyleSet* aStyleSet); + + // nsICSSLoaderObserver interface + NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasAlternate, + nsresult aStatus) override; + + void EnsureUniqueInner(); + + // Append all of this sheet's child sheets to aArray. + void AppendAllChildSheets(nsTArray& aArray); + + bool UseForPresentation(nsPresContext* aPresContext, + nsMediaQueryResultCacheKey& aKey) const; + + nsresult ReparseSheet(const nsAString& aInput); + + void SetInRuleProcessorCache() { mInRuleProcessorCache = true; } + + // Function used as a callback to rebuild our inner's child sheet + // list after we clone a unique inner for ourselves. + static bool RebuildChildList(css::Rule* aRule, void* aBuilder); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + dom::Element* GetScopeElement() const { return mScopeElement; } + void SetScopeElement(dom::Element* aScopeElement) + { + mScopeElement = aScopeElement; + } + + // WebIDL StyleSheet API + nsMediaList* Media() final; + + // WebIDL CSSStyleSheet API + // Can't be inline because we can't include ImportRule here. And can't be + // called GetOwnerRule because that would be ambiguous with the ImportRule + // version. + nsIDOMCSSRule* GetDOMOwnerRule() const final; + + void WillDirty(); + void DidDirty(); + +private: + CSSStyleSheet(const CSSStyleSheet& aCopy, + CSSStyleSheet* aParentToUse, + css::ImportRule* aOwnerRuleToUse, + nsIDocument* aDocumentToUse, + nsINode* aOwningNodeToUse); + + CSSStyleSheet(const CSSStyleSheet& aCopy) = delete; + CSSStyleSheet& operator=(const CSSStyleSheet& aCopy) = delete; + +protected: + virtual ~CSSStyleSheet(); + + void ClearRuleCascades(); + + // Add the namespace mapping from this @namespace rule to our namespace map + nsresult RegisterNamespaceRule(css::Rule* aRule); + + // Drop our reference to mRuleCollection + void DropRuleCollection(); + + // Drop our reference to mMedia + void DropMedia(); + + // Unlink our inner, if needed, for cycle collection + void UnlinkInner(); + // Traverse our inner, if needed, for cycle collection + void TraverseInner(nsCycleCollectionTraversalCallback &); + +protected: + // Internal methods which do not have security check and completeness check. + dom::CSSRuleList* GetCssRulesInternal(ErrorResult& aRv); + uint32_t InsertRuleInternal(const nsAString& aRule, + uint32_t aIndex, ErrorResult& aRv); + void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv); + + RefPtr mMedia; + RefPtr mNext; + CSSStyleSheet* mParent; // weak ref + css::ImportRule* mOwnerRule; // weak ref + + RefPtr mRuleCollection; + bool mDirty; // has been modified + bool mInRuleProcessorCache; + RefPtr mScopeElement; + + CSSStyleSheetInner* mInner; + + AutoTArray* mRuleProcessors; + nsTArray mStyleSets; + + friend class ::nsMediaList; + friend class ::nsCSSRuleProcessor; + friend class mozilla::StyleSheet; + friend struct mozilla::ChildSheetListBuilder; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(CSSStyleSheet, NS_CSS_STYLE_SHEET_IMPL_CID) + +} // namespace mozilla + +#endif /* !defined(mozilla_CSSStyleSheet_h) */ diff --git a/layout/style/CSSUnprefixingService.js b/layout/style/CSSUnprefixingService.js new file mode 100644 index 000000000..801ea198c --- /dev/null +++ b/layout/style/CSSUnprefixingService.js @@ -0,0 +1,342 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- / +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* Implementation of a service that converts certain vendor-prefixed CSS + properties to their unprefixed equivalents, for sites on a whitelist. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function CSSUnprefixingService() { +} + +CSSUnprefixingService.prototype = { + // Boilerplate: + classID: Components.ID("{f0729490-e15c-4a2f-a3fb-99e1cc946b42}"), + _xpcom_factory: XPCOMUtils.generateSingletonFactory(CSSUnprefixingService), + QueryInterface: XPCOMUtils.generateQI([Ci.nsICSSUnprefixingService]), + + // See documentation in nsICSSUnprefixingService.idl + generateUnprefixedDeclaration: function(aPropName, aRightHalfOfDecl, + aUnprefixedDecl /*out*/) { + + // Convert our input strings to lower-case, for easier string-matching. + // (NOTE: If we ever need to add support for unprefixing properties that + // have case-sensitive parts, then we should do these toLowerCase() + // conversions in a more targeted way, to avoid breaking those properties.) + aPropName = aPropName.toLowerCase(); + aRightHalfOfDecl = aRightHalfOfDecl.toLowerCase(); + + // We have several groups of supported properties: + // FIRST GROUP: Properties that can just be handled as aliases: + // ============================================================ + const propertiesThatAreJustAliases = { + "-webkit-background-size": "background-size", + "-webkit-box-flex": "flex-grow", + "-webkit-box-ordinal-group": "order", + "-webkit-box-sizing": "box-sizing", + "-webkit-transform": "transform", + "-webkit-transform-origin": "transform-origin", + }; + + let unprefixedPropName = propertiesThatAreJustAliases[aPropName]; + if (unprefixedPropName !== undefined) { + aUnprefixedDecl.value = unprefixedPropName + ":" + aRightHalfOfDecl; + return true; + } + + // SECOND GROUP: Properties that take a single keyword, where the + // unprefixed version takes a different (but analogous) set of keywords: + // ===================================================================== + const propertiesThatNeedKeywordMapping = { + "-webkit-box-align" : { + unprefixedPropName : "align-items", + valueMap : { + "start" : "flex-start", + "center" : "center", + "end" : "flex-end", + "baseline" : "baseline", + "stretch" : "stretch" + } + }, + "-webkit-box-orient" : { + unprefixedPropName : "flex-direction", + valueMap : { + "horizontal" : "row", + "inline-axis" : "row", + "vertical" : "column", + "block-axis" : "column" + } + }, + "-webkit-box-pack" : { + unprefixedPropName : "justify-content", + valueMap : { + "start" : "flex-start", + "center" : "center", + "end" : "flex-end", + "justify" : "space-between" + } + }, + }; + + let propInfo = propertiesThatNeedKeywordMapping[aPropName]; + if (typeof(propInfo) != "undefined") { + // Regexp for parsing the right half of a declaration, for keyword-valued + // properties. Divides the right half of the declaration into: + // 1) any leading whitespace + // 2) the property value (one or more alphabetical character or hyphen) + // 3) anything after that (e.g. "!important", ";") + // Then we can look up the appropriate unprefixed-property value for the + // value (part 2), and splice that together with the other parts and with + // the unprefixed property-name to make the final declaration. + const keywordValuedPropertyRegexp = /^(\s*)([a-z\-]+)(.*)/; + let parts = keywordValuedPropertyRegexp.exec(aRightHalfOfDecl); + if (!parts) { + // Failed to parse a keyword out of aRightHalfOfDecl. (It probably has + // no alphabetical characters.) + return false; + } + + let mappedKeyword = propInfo.valueMap[parts[2]]; + if (mappedKeyword === undefined) { + // We found a keyword in aRightHalfOfDecl, but we don't have a mapping + // to an equivalent keyword for the unprefixed version of the property. + return false; + } + + aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + + parts[1] + // any leading whitespace + mappedKeyword + + parts[3]; // any trailing text (e.g. !important, semicolon, etc) + + return true; + } + + // THIRD GROUP: Properties that may need arbitrary string-replacement: + // =================================================================== + const propertiesThatNeedStringReplacement = { + // "-webkit-transition" takes a multi-part value. If "-webkit-transform" + // appears as part of that value, replace it w/ "transform". + // And regardless, we unprefix the "-webkit-transition" property-name. + // (We could handle other prefixed properties in addition to 'transform' + // here, but in practice "-webkit-transform" is the main one that's + // likely to be transitioned & that we're concerned about supporting.) + "-webkit-transition": { + unprefixedPropName : "transition", + stringMap : { + "-webkit-transform" : "transform", + } + }, + }; + + propInfo = propertiesThatNeedStringReplacement[aPropName]; + if (typeof(propInfo) != "undefined") { + let newRightHalf = aRightHalfOfDecl; + for (let strToReplace in propInfo.stringMap) { + let replacement = propInfo.stringMap[strToReplace]; + newRightHalf = newRightHalf.split(strToReplace).join(replacement); + } + aUnprefixedDecl.value = propInfo.unprefixedPropName + ":" + newRightHalf; + + return true; + } + + // No known mapping for property aPropName. + return false; + }, + + // See documentation in nsICSSUnprefixingService.idl + generateUnprefixedGradientValue: function(aPrefixedFuncName, + aPrefixedFuncBody, + aUnprefixedFuncName, /*[out]*/ + aUnprefixedFuncBody /*[out]*/) { + var unprefixedFuncName, newValue; + if (aPrefixedFuncName == "-webkit-gradient") { + // Create expression for oldGradientParser: + var parts = this.oldGradientParser(aPrefixedFuncBody); + var type = parts[0].name; + newValue = this.standardizeOldGradientArgs(type, parts.slice(1)); + unprefixedFuncName = type + "-gradient"; + }else{ // we're dealing with more modern syntax - should be somewhat easier, at least for linear gradients. + // Fix three things: remove -webkit-, add 'to ' before reversed top/bottom keywords (linear) or 'at ' before position keywords (radial), recalculate deg-values + // -webkit-linear-gradient( [ [ | [top | bottom] || [left | right] ],]? [, ]+); + if (aPrefixedFuncName != "-webkit-linear-gradient" && + aPrefixedFuncName != "-webkit-radial-gradient") { + // Unrecognized prefixed gradient type + return false; + } + unprefixedFuncName = aPrefixedFuncName.replace(/-webkit-/, ''); + + // Keywords top, bottom, left, right: can be stand-alone or combined pairwise but in any order ('top left' or 'left top') + // These give the starting edge or corner in the -webkit syntax. The standardised equivalent is 'to ' plus opposite values for linear gradients, 'at ' plus same values for radial gradients + if(unprefixedFuncName.indexOf('linear') > -1){ + newValue = aPrefixedFuncBody.replace(/(top|bottom|left|right)+\s*(top|bottom|left|right)*/, function(str){ + var words = str.split(/\s+/); + for(var i=0; imSIDs & NS_STYLE_INHERIT_BIT(Variables))) { + return; + } + + if (!aRuleData->mVariables) { + aRuleData->mVariables = new CSSVariableDeclarations(*this); + } else { + for (auto iter = mVariables.Iter(); !iter.Done(); iter.Next()) { + nsDataHashtable& variables = + aRuleData->mVariables->mVariables; + const nsAString& aName = iter.Key(); + if (!variables.Contains(aName)) { + variables.Put(aName, iter.UserData()); + } + } + } +} + +void +CSSVariableDeclarations::AddVariablesToResolver( + CSSVariableResolver* aResolver) const +{ + for (auto iter = mVariables.ConstIter(); !iter.Done(); iter.Next()) { + const nsAString& name = iter.Key(); + nsString value = iter.UserData(); + if (value.EqualsLiteral(INITIAL_VALUE)) { + // Values of 'initial' are treated the same as an invalid value in the + // variable resolver. + aResolver->Put(name, EmptyString(), + eCSSTokenSerialization_Nothing, + eCSSTokenSerialization_Nothing, + false); + } else if (value.EqualsLiteral(INHERIT_VALUE) || + value.EqualsLiteral(UNSET_VALUE)) { + // Values of 'inherit' and 'unset' don't need any handling, since it means + // we just need to keep whatever value is currently in the resolver. This + // is because the specified variable declarations already have only the + // winning declaration for the variable and no longer have any of the + // others. + } else { + // At this point, we don't know what token types are at the start and end + // of the specified variable value. These will be determined later during + // the resolving process. + aResolver->Put(name, value, + eCSSTokenSerialization_Nothing, + eCSSTokenSerialization_Nothing, + false); + } + } +} + +size_t +CSSVariableDeclarations::SizeOfIncludingThis( + mozilla::MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mVariables.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mVariables.ConstIter(); !iter.Done(); iter.Next()) { + n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += iter.Data().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } + return n; +} + +} // namespace mozilla diff --git a/layout/style/CSSVariableDeclarations.h b/layout/style/CSSVariableDeclarations.h new file mode 100644 index 000000000..ebc17285b --- /dev/null +++ b/layout/style/CSSVariableDeclarations.h @@ -0,0 +1,139 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 Custom Property assignments for a Declaration at a given priority */ + +#ifndef mozilla_CSSVariableDeclarations_h +#define mozilla_CSSVariableDeclarations_h + +#include "nsDataHashtable.h" + +namespace mozilla { +class CSSVariableResolver; +} // namespace mozilla +struct nsRuleData; + +namespace mozilla { + +class CSSVariableDeclarations +{ +public: + CSSVariableDeclarations(); + CSSVariableDeclarations(const CSSVariableDeclarations& aOther); +#ifdef DEBUG + ~CSSVariableDeclarations(); +#endif + CSSVariableDeclarations& operator=(const CSSVariableDeclarations& aOther); + + /** + * Returns whether this set of variable declarations includes a variable + * with a given name. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name). + */ + bool Has(const nsAString& aName) const; + + /** + * Represents the type of a variable value. + */ + enum Type { + eTokenStream, // a stream of CSS tokens (the usual type for variables) + eInitial, // 'initial' + eInherit, // 'inherit' + eUnset // 'unset' + }; + + /** + * Gets the value of a variable in this set of variable declarations. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name). + * @param aType Out parameter into which the type of the variable value will + * be stored. + * @param aValue Out parameter into which the value of the variable will + * be stored. If the variable is 'initial', 'inherit' or 'unset', this will + * be the empty string. + * @return Whether a variable with the given name was found. When false + * is returned, aType and aValue will not be modified. + */ + bool Get(const nsAString& aName, Type& aType, nsString& aValue) const; + + /** + * Adds or modifies an existing entry in this set of variable declarations + * to have the value 'initial'. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + */ + void PutInitial(const nsAString& aName); + + /** + * Adds or modifies an existing entry in this set of variable declarations + * to have the value 'inherit'. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + */ + void PutInherit(const nsAString& aName); + + /** + * Adds or modifies an existing entry in this set of variable declarations + * to have the value 'unset'. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + */ + void PutUnset(const nsAString& aName); + + /** + * Adds or modifies an existing entry in this set of variable declarations + * to have a token stream value. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + * @param aTokenStream The CSS token stream. + */ + void PutTokenStream(const nsAString& aName, const nsString& aTokenStream); + + /** + * Removes an entry in this set of variable declarations. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose entry is to be removed. + */ + void Remove(const nsAString& aName); + + /** + * Returns the number of entries in this set of variable declarations. + */ + uint32_t Count() const { return mVariables.Count(); } + + /** + * Copies each variable value from this object into aRuleData, unless that + * variable already exists on aRuleData. + */ + void MapRuleInfoInto(nsRuleData* aRuleData); + + /** + * Copies the variables from this object into aResolver, marking them as + * specified values. + */ + void AddVariablesToResolver(CSSVariableResolver* aResolver) const; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + /** + * Adds all the variable declarations from aOther into this object. + */ + void CopyVariablesFrom(const CSSVariableDeclarations& aOther); + + nsDataHashtable mVariables; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/CSSVariableImageTable.h b/layout/style/CSSVariableImageTable.h new file mode 100644 index 000000000..26c9c7507 --- /dev/null +++ b/layout/style/CSSVariableImageTable.h @@ -0,0 +1,190 @@ +/* -*- 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/. */ + +/* A global table that tracks images referenced by CSS variables. */ + +#ifndef mozilla_CSSVariableImageTable_h +#define mozilla_CSSVariableImageTable_h + +#include "nsClassHashtable.h" +#include "nsCSSPropertyID.h" +#include "nsCSSValue.h" +#include "nsStyleContext.h" +#include "nsTArray.h" + +/** + * CSSVariableImageTable maintains a global mapping + * (nsStyleContext, nsCSSPropertyID) -> nsTArray + * which allows us to track the relationship between CSS property values + * involving variables and any images they may reference. + * + * When properties like background-image contain a normal url(), the + * Declaration's data block will hold a reference to the ImageValue. When a + * token stream is used, the Declaration only holds on to an + * nsCSSValueTokenStream object, and the ImageValue would only exist for the + * duration of nsRuleNode::WalkRuleTree, in the AutoCSSValueArray. So instead + * when we re-parse a token stream and get an ImageValue, we record it in the + * CSSVariableImageTable to keep the ImageValue alive. Such ImageValues are + * eventually freed the next time the token stream is re-parsed, or when the + * associated style context is destroyed. + * + * To add ImageValues to the CSSVariableImageTable, callers should pass a lambda + * to CSSVariableImageTable::ReplaceAll() that calls + * CSSVariableImageTable::Add() for each ImageValue that needs to be added to + * the table. When callers are sure that the ImageValues for a given + * nsStyleContext won't be needed anymore, they can call + * CSSVariableImageTable::RemoveAll() to release them. + */ + +namespace mozilla { +namespace CSSVariableImageTable { + +namespace detail { + +typedef nsTArray> ImageValueArray; +typedef nsClassHashtable, ImageValueArray> + PerPropertyImageHashtable; +typedef nsClassHashtable, PerPropertyImageHashtable> + CSSVariableImageHashtable; + +inline CSSVariableImageHashtable& GetTable() +{ + static CSSVariableImageHashtable imageTable; + return imageTable; +} + +#ifdef DEBUG +inline bool& IsReplacing() +{ + static bool isReplacing = false; + return isReplacing; +} +#endif + +} // namespace detail + +/** + * ReplaceAll() allows callers to replace the ImageValues associated with a + * (nsStyleContext, nsCSSPropertyID) pair. The memory used by the previous list of + * ImageValues is automatically released. + * + * @param aContext The style context the ImageValues are associated with. + * @param aProp The CSS property the ImageValues are associated with. + * @param aFunc A lambda that calls CSSVariableImageTable::Add() to add new + * ImageValues which will replace the old ones. + */ +template +inline void ReplaceAll(nsStyleContext* aContext, + nsCSSPropertyID aProp, + Lambda aFunc) +{ + MOZ_ASSERT(aContext); + + auto& imageTable = detail::GetTable(); + + // Clear the existing image array, if any, for this property. + { + auto* perPropertyImageTable = imageTable.Get(aContext); + auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp) + : nullptr; + if (imageList) { + imageList->ClearAndRetainStorage(); + } + } + +#ifdef DEBUG + MOZ_ASSERT(!detail::IsReplacing()); + detail::IsReplacing() = true; +#endif + + aFunc(); + +#ifdef DEBUG + detail::IsReplacing() = false; +#endif + + // Clean up. + auto* perPropertyImageTable = imageTable.Get(aContext); + auto* imageList = perPropertyImageTable ? perPropertyImageTable->Get(aProp) + : nullptr; + if (imageList) { + if (imageList->IsEmpty()) { + // We used to have an image array for this property, but now we don't. + // Remove the entry in the per-property image table for this property. + // That may then allow us to remove the entire per-property image table. + perPropertyImageTable->Remove(aProp); + if (perPropertyImageTable->Count() == 0) { + imageTable.Remove(aContext); + } + } else { + // We still have a non-empty image array for this property. Compact the + // storage it's using if possible. + imageList->Compact(); + } + } +} + +/** + * Adds a new ImageValue @aValue to the CSSVariableImageTable, which will be + * associated with @aContext and @aProp. + * + * It's illegal to call this function outside of a lambda passed to + * CSSVariableImageTable::ReplaceAll(). + */ +inline void +Add(nsStyleContext* aContext, nsCSSPropertyID aProp, css::ImageValue* aValue) +{ + MOZ_ASSERT(aValue); + MOZ_ASSERT(aContext); + MOZ_ASSERT(detail::IsReplacing()); + + auto& imageTable = detail::GetTable(); + + // Ensure there's a per-property image table for this style context. + auto* perPropertyImageTable = imageTable.Get(aContext); + if (!perPropertyImageTable) { + perPropertyImageTable = new detail::PerPropertyImageHashtable(); + imageTable.Put(aContext, perPropertyImageTable); + } + + // Ensure there's an image array for this property. + auto* imageList = perPropertyImageTable->Get(aProp); + if (!imageList) { + imageList = new detail::ImageValueArray(); + perPropertyImageTable->Put(aProp, imageList); + } + + // Append the provided ImageValue to the list. + imageList->AppendElement(aValue); +} + +/** + * Removes all ImageValues stored in the CSSVariableImageTable for the provided + * @aContext. + */ +inline void +RemoveAll(nsStyleContext* aContext) +{ + // Move all ImageValue references into removedImageList so that we can + // release them outside of any hashtable methods. (If we just call + // Remove(aContext) on the table then we can end up calling back + // re-entrantly into hashtable methods, as other style contexts + // are released.) + detail::ImageValueArray removedImages; + auto& imageTable = detail::GetTable(); + auto* perPropertyImageTable = imageTable.Get(aContext); + if (perPropertyImageTable) { + for (auto it = perPropertyImageTable->Iter(); !it.Done(); it.Next()) { + auto* imageList = it.UserData(); + removedImages.AppendElements(Move(*imageList)); + } + } + imageTable.Remove(aContext); +} + +} // namespace CSSVariableImageTable +} // namespace mozilla + +#endif // mozilla_CSSVariableImageTable_h diff --git a/layout/style/CSSVariableResolver.cpp b/layout/style/CSSVariableResolver.cpp new file mode 100644 index 000000000..0d25b747b --- /dev/null +++ b/layout/style/CSSVariableResolver.cpp @@ -0,0 +1,266 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* object that resolves CSS variables using specified and inherited variable + * values + */ + +#include "CSSVariableResolver.h" + +#include "CSSVariableDeclarations.h" +#include "CSSVariableValues.h" +#include "mozilla/PodOperations.h" +#include "mozilla/UniquePtr.h" +#include + +namespace mozilla { + +/** + * Data used by the EnumerateVariableReferences callback. Reset must be called + * on it before it is used. + */ +class EnumerateVariableReferencesData +{ +public: + explicit EnumerateVariableReferencesData(CSSVariableResolver& aResolver) + : mResolver(aResolver) + , mReferences(MakeUnique(aResolver.mVariables.Length())) + { + } + + /** + * Resets the data so that it can be passed to another call of + * EnumerateVariableReferences for a different variable. + */ + void Reset() + { + PodZero(mReferences.get(), mResolver.mVariables.Length()); + mReferencesNonExistentVariable = false; + } + + void RecordVariableReference(const nsAString& aVariableName) + { + size_t id; + if (mResolver.mVariableIDs.Get(aVariableName, &id)) { + mReferences[id] = true; + } else { + mReferencesNonExistentVariable = true; + } + } + + bool HasReferenceToVariable(size_t aID) const + { + return mReferences[aID]; + } + + bool ReferencesNonExistentVariable() const + { + return mReferencesNonExistentVariable; + } + +private: + CSSVariableResolver& mResolver; + + // Array of booleans, where each index is a variable ID. If an element is + // true, it indicates that the variable we have called + // EnumerateVariableReferences for has a reference to the variable with + // that ID. + const UniquePtr mReferences; + + // Whether the variable we have called EnumerateVariableReferences for + // references a variable that does not exist in the resolver. + bool mReferencesNonExistentVariable; +}; + +static void +RecordVariableReference(const nsAString& aVariableName, + void* aData) +{ + static_cast(aData)-> + RecordVariableReference(aVariableName); +} + +void +CSSVariableResolver::RemoveCycles(size_t v) +{ + mVariables[v].mIndex = mNextIndex; + mVariables[v].mLowLink = mNextIndex; + mVariables[v].mInStack = true; + mStack.AppendElement(v); + mNextIndex++; + + for (size_t i = 0, n = mReferences[v].Length(); i < n; i++) { + size_t w = mReferences[v][i]; + if (!mVariables[w].mIndex) { + RemoveCycles(w); + mVariables[v].mLowLink = std::min(mVariables[v].mLowLink, + mVariables[w].mLowLink); + } else if (mVariables[w].mInStack) { + mVariables[v].mLowLink = std::min(mVariables[v].mLowLink, + mVariables[w].mIndex); + } + } + + if (mVariables[v].mLowLink == mVariables[v].mIndex) { + if (mStack.LastElement() == v) { + // A strongly connected component consisting of a single variable is not + // necessarily invalid. We handle variables that reference themselves + // earlier, in CSSVariableResolver::Resolve. + mVariables[mStack.LastElement()].mInStack = false; + mStack.TruncateLength(mStack.Length() - 1); + } else { + size_t w; + do { + w = mStack.LastElement(); + mVariables[w].mValue.Truncate(0); + mVariables[w].mInStack = false; + mStack.TruncateLength(mStack.Length() - 1); + } while (w != v); + } + } +} + +void +CSSVariableResolver::ResolveVariable(size_t aID) +{ + if (mVariables[aID].mValue.IsEmpty() || mVariables[aID].mWasInherited) { + // The variable is invalid or was inherited. We can just copy the value + // and its first/last token information across. + mOutput->Put(mVariables[aID].mVariableName, + mVariables[aID].mValue, + mVariables[aID].mFirstToken, + mVariables[aID].mLastToken); + } else { + // Otherwise we need to resolve the variable references, after resolving + // all of our dependencies first. We do this even for variables that we + // know do not reference other variables so that we can find their + // first/last token. + // + // XXX We might want to do this first/last token finding during + // EnumerateVariableReferences, so that we can avoid calling + // ResolveVariableValue and parsing the value again. + for (size_t i = 0, n = mReferences[aID].Length(); i < n; i++) { + size_t j = mReferences[aID][i]; + if (aID != j && !mVariables[j].mResolved) { + ResolveVariable(j); + } + } + nsString resolvedValue; + nsCSSTokenSerializationType firstToken, lastToken; + if (!mParser.ResolveVariableValue(mVariables[aID].mValue, mOutput, + resolvedValue, firstToken, lastToken)) { + resolvedValue.Truncate(0); + } + mOutput->Put(mVariables[aID].mVariableName, resolvedValue, + firstToken, lastToken); + } + mVariables[aID].mResolved = true; +} + +void +CSSVariableResolver::Resolve(const CSSVariableValues* aInherited, + const CSSVariableDeclarations* aSpecified) +{ + MOZ_ASSERT(!mResolved); + + // The only time we would be worried about having a null aInherited is + // for the root, but in that case nsRuleNode::ComputeVariablesData will + // happen to pass in whatever we're using as mOutput for aInherited, + // which will initially be empty. + MOZ_ASSERT(aInherited); + MOZ_ASSERT(aSpecified); + + aInherited->AddVariablesToResolver(this); + aSpecified->AddVariablesToResolver(this); + + // First we look at each variable's value and record which other variables + // it references. + size_t n = mVariables.Length(); + mReferences.SetLength(n); + EnumerateVariableReferencesData data(*this); + for (size_t id = 0; id < n; id++) { + data.Reset(); + if (!mVariables[id].mWasInherited && + !mVariables[id].mValue.IsEmpty()) { + if (mParser.EnumerateVariableReferences(mVariables[id].mValue, + RecordVariableReference, + &data)) { + // Convert the boolean array of dependencies in |data| to a list + // of dependencies. + for (size_t i = 0; i < n; i++) { + if (data.HasReferenceToVariable(i)) { + mReferences[id].AppendElement(i); + } + } + // If a variable references itself, it is invalid. (RemoveCycles + // does not check for cycles consisting of a single variable, so we + // check here.) + if (data.HasReferenceToVariable(id)) { + mVariables[id].mValue.Truncate(); + } + // Also record whether it referenced any variables that don't exist + // in the resolver, so that we can ensure we still resolve its value + // in ResolveVariable, even though its mReferences list is empty. + mVariables[id].mReferencesNonExistentVariable = + data.ReferencesNonExistentVariable(); + } else { + MOZ_ASSERT(false, "EnumerateVariableReferences should not have failed " + "if we previously parsed the specified value"); + mVariables[id].mValue.Truncate(0); + } + } + } + + // Next we remove any cycles in variable references using Tarjan's strongly + // connected components finding algorithm, setting variables in cycles to + // have an invalid value. + mNextIndex = 1; + for (size_t id = 0; id < n; id++) { + if (!mVariables[id].mIndex) { + RemoveCycles(id); + MOZ_ASSERT(mStack.IsEmpty()); + } + } + + // Finally we construct the computed value for the variable by substituting + // any variable references. + for (size_t id = 0; id < n; id++) { + if (!mVariables[id].mResolved) { + ResolveVariable(id); + } + } + +#ifdef DEBUG + mResolved = true; +#endif +} + +void +CSSVariableResolver::Put(const nsAString& aVariableName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken, + bool aWasInherited) +{ + MOZ_ASSERT(!mResolved); + + size_t id; + if (mVariableIDs.Get(aVariableName, &id)) { + MOZ_ASSERT(mVariables[id].mWasInherited && !aWasInherited, + "should only overwrite inherited variables with specified " + "variables"); + mVariables[id].mValue = aValue; + mVariables[id].mFirstToken = aFirstToken; + mVariables[id].mLastToken = aLastToken; + mVariables[id].mWasInherited = aWasInherited; + } else { + id = mVariables.Length(); + mVariableIDs.Put(aVariableName, id); + mVariables.AppendElement(Variable(aVariableName, aValue, + aFirstToken, aLastToken, aWasInherited)); + } +} + +} // namespace mozilla diff --git a/layout/style/CSSVariableResolver.h b/layout/style/CSSVariableResolver.h new file mode 100644 index 000000000..f9758ca3a --- /dev/null +++ b/layout/style/CSSVariableResolver.h @@ -0,0 +1,148 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* object that resolves CSS variables using specified and inherited variable + * values + */ + +#ifndef mozilla_CSSVariableResolver_h +#define mozilla_CSSVariableResolver_h + +#include "mozilla/DebugOnly.h" +#include "nsCSSParser.h" +#include "nsCSSScanner.h" +#include "nsDataHashtable.h" +#include "nsTArray.h" + +namespace mozilla { + +class CSSVariableDeclarations; +class CSSVariableValues; +class EnumerateVariableReferencesData; + +class CSSVariableResolver +{ + friend class CSSVariableDeclarations; + friend class CSSVariableValues; + friend class EnumerateVariableReferencesData; +public: + /** + * Creates a new CSSVariableResolver that will output a set of resolved, + * computed variables into aOutput. + */ + explicit CSSVariableResolver(CSSVariableValues* aOutput) + : mOutput(aOutput) +#ifdef DEBUG + , mResolved(false) +#endif + { + MOZ_ASSERT(aOutput); + } + + /** + * Resolves the set of inherited variables from aInherited and the + * set of specified variables from aSpecified. The resolved variables + * are written into mOutput. + */ + void Resolve(const CSSVariableValues* aInherited, + const CSSVariableDeclarations* aSpecified); + +private: + struct Variable + { + Variable(const nsAString& aVariableName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken, + bool aWasInherited) + : mVariableName(aVariableName) + , mValue(aValue) + , mFirstToken(aFirstToken) + , mLastToken(aLastToken) + , mWasInherited(aWasInherited) + , mResolved(false) + , mReferencesNonExistentVariable(false) + , mInStack(false) + , mIndex(0) + , mLowLink(0) { } + + nsString mVariableName; + nsString mValue; + nsCSSTokenSerializationType mFirstToken; + nsCSSTokenSerializationType mLastToken; + + // Whether this variable came from the set of inherited variables. + bool mWasInherited; + + // Whether this variable has been resolved yet. + bool mResolved; + + // Whether this variables includes any references to non-existent variables. + bool mReferencesNonExistentVariable; + + // Bookkeeping for the cycle remover algorithm. + bool mInStack; + size_t mIndex; + size_t mLowLink; + }; + + /** + * Adds or modifies an existing entry in the set of variables to be resolved. + * This is intended to be called by the AddVariablesToResolver functions on + * the CSSVariableDeclarations and CSSVariableValues objects passed in to + * Resolve. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + * @param aValue The variable value. + * @param aFirstToken The type of token at the start of the variable value. + * @param aLastToken The type of token at the en of the variable value. + * @param aWasInherited Whether this variable came from the set of inherited + * variables. + */ + void Put(const nsAString& aVariableName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken, + bool aWasInherited); + + // Helper functions for Resolve. + void RemoveCycles(size_t aID); + void ResolveVariable(size_t aID); + + // A mapping of variable names to an ID that indexes into mVariables + // and mReferences. + nsDataHashtable mVariableIDs; + + // The set of variables. + nsTArray mVariables; + + // The list of variables that each variable references. + nsTArray > mReferences; + + // The next index to assign to a variable found during the cycle removing + // algorithm's traversal of the variable reference graph. + size_t mNextIndex; + + // Stack of variable IDs that we push to as we traverse the variable reference + // graph while looking for cycles. Variable::mInStack reflects whether a + // given variable has its ID in mStack. + nsTArray mStack; + + // CSS parser to use for parsing property values with variable references. + nsCSSParser mParser; + + // The object to output the resolved variables into. + CSSVariableValues* mOutput; + +#ifdef DEBUG + // Whether Resolve has been called. + bool mResolved; +#endif +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/CSSVariableValues.cpp b/layout/style/CSSVariableValues.cpp new file mode 100644 index 000000000..e6f1faf26 --- /dev/null +++ b/layout/style/CSSVariableValues.cpp @@ -0,0 +1,147 @@ +/* -*- 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/. */ + +/* computed CSS Variable values */ + +#include "CSSVariableValues.h" + +#include "CSSVariableResolver.h" + +namespace mozilla { + +CSSVariableValues::CSSVariableValues() +{ + MOZ_COUNT_CTOR(CSSVariableValues); +} + +CSSVariableValues::CSSVariableValues(const CSSVariableValues& aOther) +{ + MOZ_COUNT_CTOR(CSSVariableValues); + CopyVariablesFrom(aOther); +} + +#ifdef DEBUG +CSSVariableValues::~CSSVariableValues() +{ + MOZ_COUNT_DTOR(CSSVariableValues); +} +#endif + +CSSVariableValues& +CSSVariableValues::operator=(const CSSVariableValues& aOther) +{ + if (this == &aOther) { + return *this; + } + + mVariableIDs.Clear(); + mVariables.Clear(); + CopyVariablesFrom(aOther); + return *this; +} + +bool +CSSVariableValues::operator==(const CSSVariableValues& aOther) const +{ + if (mVariables.Length() != aOther.mVariables.Length()) { + return false; + } + + for (size_t thisIndex = 0; thisIndex < mVariables.Length(); ++thisIndex) { + size_t otherIndex; + if (!aOther.mVariableIDs.Get(mVariables[thisIndex].mVariableName, + &otherIndex)) { + return false; + } + const nsString& otherValue = aOther.mVariables[otherIndex].mValue; + if (!mVariables[thisIndex].mValue.Equals(otherValue)) { + return false; + } + } + + return true; +} + +size_t +CSSVariableValues::Count() const +{ + return mVariables.Length(); +} + +bool +CSSVariableValues::Get(const nsAString& aName, nsString& aValue) const +{ + size_t id; + if (!mVariableIDs.Get(aName, &id)) { + return false; + } + aValue = mVariables[id].mValue; + return true; +} + +bool +CSSVariableValues::Get(const nsAString& aName, + nsString& aValue, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken) const +{ + size_t id; + if (!mVariableIDs.Get(aName, &id)) { + return false; + } + aValue = mVariables[id].mValue; + aFirstToken = mVariables[id].mFirstToken; + aLastToken = mVariables[id].mLastToken; + return true; +} + +void +CSSVariableValues::GetVariableAt(size_t aIndex, nsAString& aName) const +{ + aName = mVariables[aIndex].mVariableName; +} + +void +CSSVariableValues::Put(const nsAString& aName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken) +{ + size_t id; + if (mVariableIDs.Get(aName, &id)) { + mVariables[id].mValue = aValue; + mVariables[id].mFirstToken = aFirstToken; + mVariables[id].mLastToken = aLastToken; + } else { + id = mVariables.Length(); + mVariableIDs.Put(aName, id); + mVariables.AppendElement(Variable(aName, aValue, aFirstToken, aLastToken)); + } +} + +void +CSSVariableValues::CopyVariablesFrom(const CSSVariableValues& aOther) +{ + for (size_t i = 0, n = aOther.mVariables.Length(); i < n; i++) { + Put(aOther.mVariables[i].mVariableName, + aOther.mVariables[i].mValue, + aOther.mVariables[i].mFirstToken, + aOther.mVariables[i].mLastToken); + } +} + +void +CSSVariableValues::AddVariablesToResolver(CSSVariableResolver* aResolver) const +{ + for (size_t i = 0, n = mVariables.Length(); i < n; i++) { + aResolver->Put(mVariables[i].mVariableName, + mVariables[i].mValue, + mVariables[i].mFirstToken, + mVariables[i].mLastToken, + true); + } +} + +} // namespace mozilla diff --git a/layout/style/CSSVariableValues.h b/layout/style/CSSVariableValues.h new file mode 100644 index 000000000..e26566be1 --- /dev/null +++ b/layout/style/CSSVariableValues.h @@ -0,0 +1,147 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* computed CSS Variable values */ + +#ifndef mozilla_CSSVariableValues_h +#define mozilla_CSSVariableValues_h + +#include "nsCSSScanner.h" +#include "nsDataHashtable.h" +#include "nsTArray.h" + +namespace mozilla { + +class CSSVariableResolver; + +class CSSVariableValues +{ +public: + CSSVariableValues(); + CSSVariableValues(const CSSVariableValues& aOther); +#ifdef DEBUG + ~CSSVariableValues(); +#endif + CSSVariableValues& operator=(const CSSVariableValues& aOther); + + bool operator==(const CSSVariableValues& aOther) const; + bool operator!=(const CSSVariableValues& aOther) const + { return !(*this == aOther); } + + /** + * Gets the value of a variable in this set of computed variables. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name). + * @param aValue Out parameter into which the value of the variable will + * be stored. + * @return Whether a variable with the given name was found. When false + * is returned, aValue will not be modified. + */ + bool Get(const nsAString& aName, nsString& aValue) const; + + /** + * Gets the value of a variable in this set of computed variables, along + * with information on the types of tokens that appear at the start and + * end of the token stream. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name). + * @param aValue Out parameter into which the value of the variable will + * be stored. + * @param aFirstToken The type of token at the start of the variable value. + * @param aLastToken The type of token at the en of the variable value. + * @return Whether a variable with the given name was found. When false + * is returned, aValue, aFirstToken and aLastToken will not be modified. + */ + bool Get(const nsAString& aName, + nsString& aValue, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken) const; + + /** + * Gets the name of the variable at the given index. + * + * Variables on this object are ordered, and that order is just determined + * based on the order that they are added to the object. A consistent + * ordering is required for CSSDeclaration objects in the DOM. + * CSSDeclarations expose property names as indexed properties, which need to + * be stable. + * + * @param aIndex The index of the variable to get. + * @param aName Out parameter into which the name of the variable will be + * stored. + */ + void GetVariableAt(size_t aIndex, nsAString& aName) const; + + /** + * Gets the number of variables stored on this object. + */ + size_t Count() const; + + /** + * Adds or modifies an existing entry in this set of variable values. + * + * @param aName The variable name (not including any "--" prefix that would + * be part of the custom property name) whose value is to be set. + * @param aValue The variable value. + * @param aFirstToken The type of token at the start of the variable value. + * @param aLastToken The type of token at the en of the variable value. + */ + void Put(const nsAString& aName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken); + + /** + * Copies the variables from this object into aResolver, marking them as + * computed, inherited values. + */ + void AddVariablesToResolver(CSSVariableResolver* aResolver) const; + +private: + struct Variable + { + Variable() + : mFirstToken(eCSSTokenSerialization_Nothing) + , mLastToken(eCSSTokenSerialization_Nothing) + {} + + Variable(const nsAString& aVariableName, + nsString aValue, + nsCSSTokenSerializationType aFirstToken, + nsCSSTokenSerializationType aLastToken) + : mVariableName(aVariableName) + , mValue(aValue) + , mFirstToken(aFirstToken) + , mLastToken(aLastToken) + {} + + nsString mVariableName; + nsString mValue; + nsCSSTokenSerializationType mFirstToken; + nsCSSTokenSerializationType mLastToken; + }; + + /** + * Adds all the variables from aOther into this object. + */ + void CopyVariablesFrom(const CSSVariableValues& aOther); + + /** + * Map of variable names to IDs. Variable IDs are indexes into + * mVariables. + */ + nsDataHashtable mVariableIDs; + + /** + * Array of variables, indexed by variable ID. + */ + nsTArray mVariables; +}; + +} // namespace mozilla + +#endif diff --git a/layout/style/CounterStyleManager.cpp b/layout/style/CounterStyleManager.cpp new file mode 100644 index 000000000..bccc9b836 --- /dev/null +++ b/layout/style/CounterStyleManager.cpp @@ -0,0 +1,2135 @@ +/* -*- 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 "mozilla/ArenaObjectID.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/Types.h" +#include "mozilla/WritingModes.h" +#include "nsCSSRules.h" +#include "nsString.h" +#include "nsStyleSet.h" +#include "nsTArray.h" +#include "nsTHashtable.h" +#include "nsUnicodeProperties.h" +#include "mozilla/StyleSetHandle.h" +#include "mozilla/StyleSetHandleInlines.h" + +namespace mozilla { + +struct AdditiveSymbol +{ + CounterValue weight; + nsString symbol; +}; + +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, + nsSubstring& aResult, + const nsTArray& aSymbols) +{ + MOZ_ASSERT(aSymbols.Length() >= 1, + "No symbol available for cyclic counter."); + auto n = aSymbols.Length(); + CounterValue index = (aOrdinal - 1) % n; + aResult = aSymbols[index >= 0 ? index : index + n]; + return true; +} + +static bool +GetFixedCounterText(CounterValue aOrdinal, + nsSubstring& aResult, + CounterValue aStart, + const nsTArray& 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, + nsSubstring& aResult, + const nsTArray& 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, + nsSubstring& aResult, + const nsTArray& 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, + nsSubstring& aResult, + const nsTArray& 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, + nsSubstring& aResult, + const nsTArray& aSymbols) +{ + MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); + + if (aOrdinal == 0) { + const AdditiveSymbol& last = aSymbols.LastElement(); + 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, nsSubstring& 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 = { + { // digit + 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, + 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x5341, 0x767e, 0x5343 }, // unit + { 0x4e07, 0x5104 }, // unit10K + JAPANESE, // lang + true // informal +}; +static const CJKIdeographicData gDataJapaneseFormal = { + { // digit + 0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db, + 0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x62fe, 0x767e, 0x9621 }, // unit + { 0x842c, 0x5104 }, // unit10K + JAPANESE, // lang + false // informal +}; +static const CJKIdeographicData gDataKoreanHangulFormal = { + { // digit + 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, + 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c + }, + { 0xc2ed, 0xbc31, 0xcc9c }, // unit + { 0xb9cc, 0xc5b5 }, // unit10K + KOREAN, // lang + false // informal +}; +static const CJKIdeographicData gDataKoreanHanjaInformal = { + { // digit + 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, + 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x5341, 0x767e, 0x5343 }, // unit + { 0x842c, 0x5104 }, // unit10K + KOREAN, // lang + true // informal +}; +static const CJKIdeographicData gDataKoreanHanjaFormal = { + { // digit + 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db, + 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x62fe, 0x767e, 0x4edf }, // unit + { 0x842c, 0x5104 }, // unit10K + KOREAN, // lang + false // informal +}; +static const CJKIdeographicData gDataSimpChineseInformal = { + { // digit + 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, + 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x5341, 0x767e, 0x5343 }, // unit + { 0x4e07, 0x4ebf }, // unit10K + CHINESE, // lang + true // informal +}; +static const CJKIdeographicData gDataSimpChineseFormal = { + { // digit + 0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086, + 0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396 + }, + { 0x62fe, 0x4f70, 0x4edf }, // unit + { 0x4e07, 0x4ebf }, // unit10K + CHINESE, // lang + false // informal +}; +static const CJKIdeographicData gDataTradChineseInformal = { + { // digit + 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, + 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d + }, + { 0x5341, 0x767e, 0x5343 }, // unit + { 0x842c, 0x5104 }, // unit10K + CHINESE, // lang + true // informal +}; +static const CJKIdeographicData gDataTradChineseFormal = { + { // digit + 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086, + 0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396 + }, + { 0x62fe, 0x4f70, 0x4edf }, // unit + { 0x842c, 0x5104 }, // unit10K + CHINESE, // lang + false // informal +}; + +static bool +CJKIdeographicToText(CounterValue aOrdinal, nsSubstring& 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::Type>(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, nsSubstring& 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, nsSubstring& 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.Insert(NS_LITERAL_STRING("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 uint8_t +GetDefaultSpeakAsForSystem(uint8_t aSystem) +{ + MOZ_ASSERT(aSystem != NS_STYLE_COUNTER_SYSTEM_EXTENDS, + "Extends system does not have static default speak-as"); + switch (aSystem) { + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + return NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT; + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + return NS_STYLE_COUNTER_SPEAKAS_BULLETS; + default: + return NS_STYLE_COUNTER_SPEAKAS_NUMBERS; + } +} + +static bool +SystemUsesNegativeSign(uint8_t aSystem) +{ + MOZ_ASSERT(aSystem != NS_STYLE_COUNTER_SYSTEM_EXTENDS, + "Cannot check this for extending style"); + switch (aSystem) { + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: + return true; + default: + return false; + } +} + +class BuiltinCounterStyle : public CounterStyle +{ +public: + friend class CounterStyleManager; + + // will be initialized by CounterStyleManager::InitializeBuiltinCounterStyles + constexpr BuiltinCounterStyle() + : CounterStyle(NS_STYLE_LIST_STYLE_NONE) + { + } + +protected: + constexpr explicit BuiltinCounterStyle(int32_t aStyle) + : CounterStyle(aStyle) + { + } + +public: + virtual void GetStyleName(nsSubstring& aResult) override; + virtual void GetPrefix(nsSubstring& aResult) override; + virtual void GetSuffix(nsSubstring& aResult) override; + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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 uint8_t GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL) override; + + // Builtin counter style does not need refcount at all + NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return 2; } + NS_IMETHOD_(MozExternalRefCountType) Release() override { return 2; } +}; + +/* virtual */ void +BuiltinCounterStyle::GetStyleName(nsSubstring& aResult) +{ + MOZ_ASSERT(mStyle != NS_STYLE_LIST_STYLE_CUSTOM); + const nsAFlatCString& str = + nsCSSProps::ValueToKeyword(mStyle, nsCSSProps::kListStyleKTable); + MOZ_ASSERT(!str.IsEmpty()); + aResult.Assign(NS_ConvertUTF8toUTF16(str)); +} + +/* virtual */ void +BuiltinCounterStyle::GetPrefix(nsSubstring& aResult) +{ + aResult.Truncate(); +} + +/* virtual */ void +BuiltinCounterStyle::GetSuffix(nsSubstring& aResult) +{ + switch (mStyle) { + case NS_STYLE_LIST_STYLE_NONE: + aResult.Truncate(); + break; + + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC: + aResult = ' '; + break; + + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + aResult = 0x3001; + break; + + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + 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 = 0x25fe; +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, + nsSubstring& aResult, + bool& aIsBullet) +{ + switch (mStyle) { + case NS_STYLE_LIST_STYLE_NONE: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: { + // 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 NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + 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 NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + aResult.before = gJapaneseNegative; + break; + + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + aResult.before = gKoreanNegative; + break; + + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + aResult.before = gSimpChineseNegative; + break; + + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + aResult.before = gTradChineseNegative; + break; + + default: + aResult.before.AssignLiteral(u"-"); + } + aResult.after.Truncate(); +} + +/* virtual */ bool +BuiltinCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) +{ + switch (mStyle) { + default: + // cyclic + case NS_STYLE_LIST_STYLE_NONE: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + // use DecimalToText + case NS_STYLE_LIST_STYLE_DECIMAL: + // use CJKIdeographicToText + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + return true; + + // use EthiopicToText + case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC: + return aOrdinal >= 1; + + // use HebrewToText + case NS_STYLE_LIST_STYLE_HEBREW: + return aOrdinal >= 1 && aOrdinal <= 999999; + } +} + +/* virtual */ bool +BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) +{ + switch (mStyle) { + // cyclic: + case NS_STYLE_LIST_STYLE_NONE: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + // numeric: + case NS_STYLE_LIST_STYLE_DECIMAL: + return true; + + // additive: + case NS_STYLE_LIST_STYLE_HEBREW: + return aOrdinal >= 0; + + // complex predefined: + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC: + return IsOrdinalInRange(aOrdinal); + + default: + NS_NOTREACHED("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 */ uint8_t +BuiltinCounterStyle::GetSpeakAs() +{ + switch (mStyle) { + case NS_STYLE_LIST_STYLE_NONE: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + return NS_STYLE_COUNTER_SPEAKAS_BULLETS; + default: + return NS_STYLE_COUNTER_SPEAKAS_NUMBERS; + } +} + +/* virtual */ bool +BuiltinCounterStyle::UseNegativeSign() +{ + switch (mStyle) { + case NS_STYLE_LIST_STYLE_NONE: + case NS_STYLE_LIST_STYLE_DISC: + case NS_STYLE_LIST_STYLE_CIRCLE: + case NS_STYLE_LIST_STYLE_SQUARE: + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + return false; + default: + return true; + } +} + +/* virtual */ bool +BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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 NS_STYLE_LIST_STYLE_NONE: + aResult.Truncate(); + return true; + case NS_STYLE_LIST_STYLE_DISC: + aResult.Assign(kDiscCharacter); + return true; + case NS_STYLE_LIST_STYLE_CIRCLE: + aResult.Assign(kCircleCharacter); + return true; + case NS_STYLE_LIST_STYLE_SQUARE: + aResult.Assign(kSquareCharacter); + return true; + case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED: + if (aWritingMode.IsVertical()) { + aResult.Assign(kDownPointingCharacter); + } else if (aWritingMode.IsBidiLTR()) { + aResult.Assign(kRightPointingCharacter); + } else { + aResult.Assign(kLeftPointingCharacter); + } + return true; + case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN: + if (!aWritingMode.IsVertical()) { + aResult.Assign(kDownPointingCharacter); + } else if (aWritingMode.IsVerticalLR()) { + aResult.Assign(kRightPointingCharacter); + } else { + aResult.Assign(kLeftPointingCharacter); + } + return true; + + case NS_STYLE_LIST_STYLE_DECIMAL: + return DecimalToText(aOrdinal, aResult); + + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseInformal); + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseFormal); + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseInformal); + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseFormal); + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseInformal); + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseFormal); + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHangulFormal); + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaInformal); + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaFormal); + + case NS_STYLE_LIST_STYLE_HEBREW: + aIsRTL = true; + return HebrewToText(aOrdinal, aResult); + + case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC: + return EthiopicToText(aOrdinal, aResult); + + default: + NS_NOTREACHED("Unknown builtin counter style"); + return false; + } +} + +class DependentBuiltinCounterStyle final : public BuiltinCounterStyle +{ +private: + ~DependentBuiltinCounterStyle() {} +public: + DependentBuiltinCounterStyle(int32_t aStyle, CounterStyleManager* aManager) + : BuiltinCounterStyle(aStyle), + mManager(aManager) + { + NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style"); + MOZ_ASSERT(!IsCustomStyle(), "Not a builtin style"); + } + + virtual CounterStyle* GetFallback() override; + + // DependentBuiltinCounterStyle is managed in the same way as + // CustomCounterStyle. + NS_IMETHOD_(MozExternalRefCountType) AddRef() override; + NS_IMETHOD_(MozExternalRefCountType) Release() override; + + void* operator new(size_t sz, nsPresContext* aPresContext) + { + return aPresContext->PresShell()->AllocateByObjectID( + eArenaObjectID_DependentBuiltinCounterStyle, sz); + } + +private: + void Destroy() + { + nsIPresShell* shell = mManager->PresContext()->PresShell(); + this->~DependentBuiltinCounterStyle(); + shell->FreeByObjectID(eArenaObjectID_DependentBuiltinCounterStyle, this); + } + + CounterStyleManager* mManager; + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +NS_IMPL_ADDREF(DependentBuiltinCounterStyle) +NS_IMPL_RELEASE_WITH_DESTROY(DependentBuiltinCounterStyle, Destroy()) + +/* virtual */ CounterStyle* +DependentBuiltinCounterStyle::GetFallback() +{ + switch (GetStyle()) { + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + // 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->BuildCounterStyle(NS_LITERAL_STRING("cjk-decimal")); + default: + NS_NOTREACHED("Not a valid dependent builtin style"); + return BuiltinCounterStyle::GetFallback(); + } +} + +class CustomCounterStyle final : public CounterStyle +{ +private: + ~CustomCounterStyle() {} +public: + CustomCounterStyle(const nsAString& aName, + CounterStyleManager* aManager, + nsCSSCounterStyleRule* aRule) + : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM), + mName(aName), + mManager(aManager), + mRule(aRule), + mRuleGeneration(aRule->GetGeneration()), + mSystem(aRule->GetSystem()), + 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(); + + nsCSSCounterStyleRule* GetRule() const { return mRule; } + uint32_t GetRuleGeneration() const { return mRuleGeneration; } + + virtual void GetStyleName(nsSubstring& aResult) override; + virtual void GetPrefix(nsSubstring& aResult) override; + virtual void GetSuffix(nsSubstring& aResult) override; + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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 uint8_t GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual void CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL) override; + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL) override; + + bool IsExtendsSystem() + { + return mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS; + } + + // CustomCounterStyle should be reference-counted because it may be + // dereferenced from the manager but still referenced by nodes and + // frames before the style change is propagated. + NS_IMETHOD_(MozExternalRefCountType) AddRef() override; + NS_IMETHOD_(MozExternalRefCountType) Release() override; + + void* operator new(size_t sz, nsPresContext* aPresContext) + { + return aPresContext->PresShell()->AllocateByObjectID( + eArenaObjectID_CustomCounterStyle, sz); + } + +private: + void Destroy() + { + nsIPresShell* shell = mManager->PresContext()->PresShell(); + this->~CustomCounterStyle(); + shell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this); + } + + const nsTArray& GetSymbols(); + const nsTArray& 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. + uint8_t GetSpeakAsAutoValue(); + void ComputeRawSpeakAs(uint8_t& aSpeakAs, + CounterStyle*& aSpeakAsCounter); + CounterStyle* ComputeSpeakAs(); + + CounterStyle* ComputeExtends(); + CounterStyle* GetExtends(); + CounterStyle* GetExtendsRoot(); + + nsString mName; + + // 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; + + uint8_t mSystem; + // GetSpeakAs will ensure that private member mSpeakAs is initialized before used + MOZ_INIT_OUTSIDE_CTOR uint8_t 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. + nsTArray mSymbols; + nsTArray 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; + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +NS_IMPL_ADDREF(CustomCounterStyle) +NS_IMPL_RELEASE_WITH_DESTROY(CustomCounterStyle, Destroy()) + +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 = mRule->GetGeneration(); +} + +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::GetStyleName(nsSubstring& aResult) +{ + aResult.Assign(mName); +} + +/* virtual */ void +CustomCounterStyle::GetPrefix(nsSubstring& aResult) +{ + if (!(mFlags & FLAG_PREFIX_INITED)) { + mFlags |= FLAG_PREFIX_INITED; + + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Prefix); + if (value.UnitHasStringValue()) { + value.GetStringValue(mPrefix); + } else if (IsExtendsSystem()) { + GetExtends()->GetPrefix(mPrefix); + } else { + mPrefix.Truncate(); + } + } + aResult = mPrefix; +} + +/* virtual */ void +CustomCounterStyle::GetSuffix(nsSubstring& aResult) +{ + if (!(mFlags & FLAG_SUFFIX_INITED)) { + mFlags |= FLAG_SUFFIX_INITED; + + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Suffix); + if (value.UnitHasStringValue()) { + value.GetStringValue(mSuffix); + } else if (IsExtendsSystem()) { + GetExtends()->GetSuffix(mSuffix); + } else { + mSuffix.AssignLiteral(u". "); + } + } + aResult = mSuffix; +} + +/* virtual */ void +CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsBullet) +{ + if (GetSpeakAs() != NS_STYLE_COUNTER_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 NS_STYLE_COUNTER_SYSTEM_CYCLIC: + // Only use ::-moz-list-bullet for cyclic system + return true; + case NS_STYLE_COUNTER_SYSTEM_EXTENDS: + return GetExtendsRoot()->IsBullet(); + default: + return false; + } +} + +/* virtual */ void +CustomCounterStyle::GetNegative(NegativeType& aResult) +{ + if (!(mFlags & FLAG_NEGATIVE_INITED)) { + mFlags |= FLAG_NEGATIVE_INITED; + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Negative); + switch (value.GetUnit()) { + case eCSSUnit_Ident: + case eCSSUnit_String: + value.GetStringValue(mNegative.before); + mNegative.after.Truncate(); + break; + case eCSSUnit_Pair: { + const nsCSSValuePair& pair = value.GetPairValue(); + pair.mXValue.GetStringValue(mNegative.before); + pair.mYValue.GetStringValue(mNegative.after); + break; + } + default: { + if (IsExtendsSystem()) { + GetExtends()->GetNegative(mNegative); + } else { + mNegative.before.AssignLiteral(u"-"); + mNegative.after.Truncate(); + } + } + } + } + aResult = mNegative; +} + +static inline bool +IsRangeValueInfinite(const nsCSSValue& aValue) +{ + return aValue.GetUnit() == eCSSUnit_Enumerated && + aValue.GetIntValue() == NS_STYLE_COUNTER_RANGE_INFINITE; +} + +/* virtual */ bool +CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) +{ + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Range); + if (value.GetUnit() == eCSSUnit_PairList) { + for (const nsCSSValuePairList* item = value.GetPairListValue(); + item != nullptr; item = item->mNext) { + const nsCSSValue& lowerBound = item->mXValue; + const nsCSSValue& upperBound = item->mYValue; + if ((IsRangeValueInfinite(lowerBound) || + aOrdinal >= lowerBound.GetIntValue()) && + (IsRangeValueInfinite(upperBound) || + aOrdinal <= upperBound.GetIntValue())) { + return true; + } + } + return false; + } else if (IsExtendsSystem() && value.GetUnit() == eCSSUnit_None) { + // Only use the range of extended style when 'range' is not specified. + return GetExtends()->IsOrdinalInRange(aOrdinal); + } + return IsOrdinalInAutoRange(aOrdinal); +} + +/* virtual */ bool +CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) +{ + switch (mSystem) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + case NS_STYLE_COUNTER_SYSTEM_FIXED: + return true; + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + return aOrdinal >= 1; + case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: + return aOrdinal >= 0; + case NS_STYLE_COUNTER_SYSTEM_EXTENDS: + return GetExtendsRoot()->IsOrdinalInAutoRange(aOrdinal); + default: + NS_NOTREACHED("Invalid system for computing auto value."); + return false; + } +} + +/* virtual */ void +CustomCounterStyle::GetPad(PadType& aResult) +{ + if (!(mFlags & FLAG_PAD_INITED)) { + mFlags |= FLAG_PAD_INITED; + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Pad); + if (value.GetUnit() == eCSSUnit_Pair) { + const nsCSSValuePair& pair = value.GetPairValue(); + mPad.width = pair.mXValue.GetIntValue(); + pair.mYValue.GetStringValue(mPad.symbol); + } else if (IsExtendsSystem()) { + GetExtends()->GetPad(mPad); + } else { + mPad.width = 0; + mPad.symbol.Truncate(); + } + } + aResult = mPad; +} + +/* virtual */ CounterStyle* +CustomCounterStyle::GetFallback() +{ + if (!mFallback) { + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_Fallback); + if (value.UnitHasStringValue()) { + mFallback = mManager->BuildCounterStyle( + nsDependentString(value.GetStringBufferValue())); + } else if (IsExtendsSystem()) { + mFallback = GetExtends()->GetFallback(); + } else { + mFallback = CounterStyleManager::GetDecimalStyle(); + } + } + return mFallback; +} + +/* virtual */ uint8_t +CustomCounterStyle::GetSpeakAs() +{ + if (!(mFlags & FLAG_SPEAKAS_INITED)) { + ComputeSpeakAs(); + } + return mSpeakAs; +} + +/* virtual */ bool +CustomCounterStyle::UseNegativeSign() +{ + if (mSystem == NS_STYLE_COUNTER_SYSTEM_EXTENDS) { + return GetExtendsRoot()->UseNegativeSign(); + } + return SystemUsesNegativeSign(mSystem); +} + +/* virtual */ void +CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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, + nsSubstring& aResult, + bool& aIsRTL) +{ + switch (mSystem) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + return GetCyclicCounterText(aOrdinal, aResult, GetSymbols()); + case NS_STYLE_COUNTER_SYSTEM_FIXED: { + int32_t start = mRule->GetSystemArgument().GetIntValue(); + return GetFixedCounterText(aOrdinal, aResult, start, GetSymbols()); + } + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + return GetSymbolicCounterText(aOrdinal, aResult, GetSymbols()); + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols()); + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + return GetNumericCounterText(aOrdinal, aResult, GetSymbols()); + case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: + return GetAdditiveCounterText(aOrdinal, aResult, GetAdditiveSymbols()); + case NS_STYLE_COUNTER_SYSTEM_EXTENDS: + return GetExtendsRoot()-> + GetInitialCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); + default: + NS_NOTREACHED("Invalid system."); + return false; + } +} + +const nsTArray& +CustomCounterStyle::GetSymbols() +{ + if (mSymbols.IsEmpty()) { + const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_Symbols); + for (const nsCSSValueList* item = values.GetListValue(); + item; item = item->mNext) { + nsString* symbol = mSymbols.AppendElement(); + item->mValue.GetStringValue(*symbol); + } + mSymbols.Compact(); + } + return mSymbols; +} + +const nsTArray& +CustomCounterStyle::GetAdditiveSymbols() +{ + if (mAdditiveSymbols.IsEmpty()) { + const nsCSSValue& values = mRule->GetDesc(eCSSCounterDesc_AdditiveSymbols); + for (const nsCSSValuePairList* item = values.GetPairListValue(); + item; item = item->mNext) { + AdditiveSymbol* symbol = mAdditiveSymbols.AppendElement(); + symbol->weight = item->mXValue.GetIntValue(); + item->mYValue.GetStringValue(symbol->symbol); + } + mAdditiveSymbols.Compact(); + } + return mAdditiveSymbols; +} + +// This method is used to provide the computed value for 'auto'. +uint8_t +CustomCounterStyle::GetSpeakAsAutoValue() +{ + uint8_t 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(uint8_t& aSpeakAs, + CounterStyle*& aSpeakAsCounter) +{ + NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED), + "ComputeRawSpeakAs is called with speak-as inited."); + + const nsCSSValue& value = mRule->GetDesc(eCSSCounterDesc_SpeakAs); + switch (value.GetUnit()) { + case eCSSUnit_Auto: + aSpeakAs = GetSpeakAsAutoValue(); + break; + case eCSSUnit_Enumerated: + aSpeakAs = value.GetIntValue(); + break; + case eCSSUnit_Ident: + aSpeakAs = NS_STYLE_COUNTER_SPEAKAS_OTHER; + aSpeakAsCounter = mManager->BuildCounterStyle( + nsDependentString(value.GetStringBufferValue())); + break; + case eCSSUnit_Null: { + 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: + NS_NOTREACHED("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 == NS_STYLE_COUNTER_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 != NS_STYLE_COUNTER_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; + } + + const nsCSSValue& value = mRule->GetSystemArgument(); + CounterStyle* nextCounter = mManager->BuildCounterStyle( + nsDependentString(value.GetStringBufferValue())); + 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 nsSubstring& aContent) + : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM) + , mSingleString(true) + , mSystem(NS_STYLE_COUNTER_SYSTEM_CYCLIC) +{ + mSymbols.SetCapacity(1); + mSymbols.AppendElement(aContent); +} + +AnonymousCounterStyle::AnonymousCounterStyle(const nsCSSValue::Array* aParams) + : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM) + , mSingleString(false) + , mSystem(aParams->Item(0).GetIntValue()) +{ + for (const nsCSSValueList* item = aParams->Item(1).GetListValue(); + item; item = item->mNext) { + item->mValue.GetStringValue(*mSymbols.AppendElement()); + } + mSymbols.Compact(); +} + +/* virtual */ void +AnonymousCounterStyle::GetStyleName(nsAString& aResult) +{ + aResult.Truncate(); +} + +/* virtual */ void +AnonymousCounterStyle::GetPrefix(nsAString& aResult) +{ + aResult.Truncate(); +} + +/* virtual */ void +AnonymousCounterStyle::GetSuffix(nsAString& aResult) +{ + if (IsSingleString()) { + aResult.Truncate(); + } else { + aResult = ' '; + } +} + +/* virtual */ bool +AnonymousCounterStyle::IsBullet() +{ + switch (mSystem) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + // Only use ::-moz-list-bullet for cyclic system + return true; + default: + return false; + } +} + +/* virtual */ void +AnonymousCounterStyle::GetNegative(NegativeType& aResult) +{ + aResult.before.AssignLiteral(u"-"); + aResult.after.Truncate(); +} + +/* virtual */ bool +AnonymousCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) +{ + switch (mSystem) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + case NS_STYLE_COUNTER_SYSTEM_FIXED: + return true; + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + return aOrdinal >= 1; + default: + NS_NOTREACHED("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(); +} + +/* virtual */ uint8_t +AnonymousCounterStyle::GetSpeakAs() +{ + return GetDefaultSpeakAsForSystem(mSystem); +} + +/* virtual */ bool +AnonymousCounterStyle::UseNegativeSign() +{ + return SystemUsesNegativeSign(mSystem); +} + +/* virtual */ bool +AnonymousCounterStyle::GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsRTL) +{ + switch (mSystem) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + return GetCyclicCounterText(aOrdinal, aResult, mSymbols); + case NS_STYLE_COUNTER_SYSTEM_FIXED: + return GetFixedCounterText(aOrdinal, aResult, 1, mSymbols); + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + return GetSymbolicCounterText(aOrdinal, aResult, mSymbols); + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + return GetAlphabeticCounterText(aOrdinal, aResult, mSymbols); + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + return GetNumericCounterText(aOrdinal, aResult, mSymbols); + default: + NS_NOTREACHED("Invalid system."); + return false; + } +} + +bool +CounterStyle::IsDependentStyle() const +{ + switch (mStyle) { + // CustomCounterStyle + case NS_STYLE_LIST_STYLE_CUSTOM: + // DependentBuiltinCounterStyle + case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: + case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: + case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: + case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: + return true; + + // BuiltinCounterStyle + default: + return false; + } +} + +void +CounterStyle::GetCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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) { + PadType pad; + GetPad(pad); + // We have to calculate the difference here since suffix part of negative + // sign may be appended to initialText later. + int32_t diff = pad.width - + unicode::CountGraphemeClusters(initialText.Data(), + initialText.Length()); + 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); + } + 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, + nsSubstring& aResult, + bool& aIsBullet) +{ + bool isRTL; // we don't care about direction for spoken text + aIsBullet = false; + switch (GetSpeakAs()) { + case NS_STYLE_COUNTER_SPEAKAS_BULLETS: + aResult.Assign(kDiscCharacter); + aIsBullet = true; + break; + case NS_STYLE_COUNTER_SPEAKAS_NUMBERS: + DecimalToText(aOrdinal, aResult); + break; + case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT: + // we currently do not actually support 'spell-out', + // so 'words' is used instead. + case NS_STYLE_COUNTER_SPEAKAS_WORDS: + GetCounterText(aOrdinal, WritingMode(), aResult, isRTL); + break; + case NS_STYLE_COUNTER_SPEAKAS_OTHER: + // This should be processed by CustomCounterStyle + NS_NOTREACHED("Invalid speak-as value"); + break; + default: + NS_NOTREACHED("Unknown speak-as value"); + break; + } +} + +/* virtual */ void +CounterStyle::CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsAString& aResult, + bool& aIsRTL) +{ + GetFallback()->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); +} + +static BuiltinCounterStyle gBuiltinStyleTable[NS_STYLE_LIST_STYLE__MAX]; + +CounterStyleManager::CounterStyleManager(nsPresContext* aPresContext) + : mPresContext(aPresContext) +{ + // Insert the static styles into cache table + mCacheTable.Put(NS_LITERAL_STRING("none"), GetNoneStyle()); + mCacheTable.Put(NS_LITERAL_STRING("decimal"), GetDecimalStyle()); +} + +CounterStyleManager::~CounterStyleManager() +{ + MOZ_ASSERT(!mPresContext, "Disconnect should have been called"); +} + +/* static */ void +CounterStyleManager::InitializeBuiltinCounterStyles() +{ + for (uint32_t i = 0; i < NS_STYLE_LIST_STYLE__MAX; ++i) { + gBuiltinStyleTable[i].mStyle = i; + } +} + +void +CounterStyleManager::Disconnect() +{ +#ifdef DEBUG + for (auto iter = mCacheTable.Iter(); !iter.Done(); iter.Next()) { + CounterStyle* style = iter.UserData(); + style->AddRef(); + auto refcnt = style->Release(); + NS_ASSERTION(!style->IsDependentStyle() || refcnt == 1, + "Counter style is still referenced by other objects."); + } +#endif + mCacheTable.Clear(); + mPresContext = nullptr; +} + +CounterStyle* +CounterStyleManager::BuildCounterStyle(const nsSubstring& aName) +{ + CounterStyle* data = mCacheTable.GetWeak(aName); + if (data) { + return data; + } + + // It is intentional that the predefined names are case-insensitive + // but the user-defined names case-sensitive. + // XXXheycam ServoStyleSets do not support custom counter styles yet. + StyleSetHandle styleSet = mPresContext->StyleSet(); + NS_ASSERTION(styleSet->IsGecko(), + "stylo: ServoStyleSets do not support custom counter " + "styles yet"); + nsCSSCounterStyleRule* rule = styleSet->IsGecko() ? + styleSet->AsGecko()->CounterStyleRuleForName(aName) : nullptr; + if (rule) { + data = new (mPresContext) CustomCounterStyle(aName, this, rule); + } else { + int32_t type; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aName); + if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, type)) { + if (gBuiltinStyleTable[type].IsDependentStyle()) { + data = new (mPresContext) DependentBuiltinCounterStyle(type, this); + } else { + data = GetBuiltinStyle(type); + } + } + } + if (!data) { + data = GetDecimalStyle(); + } + mCacheTable.Put(aName, data); + return data; +} + +/* static */ CounterStyle* +CounterStyleManager::GetBuiltinStyle(int32_t aStyle) +{ + MOZ_ASSERT(0 <= aStyle && aStyle < NS_STYLE_LIST_STYLE__MAX, + "Require a valid builtin style constant"); + MOZ_ASSERT(!gBuiltinStyleTable[aStyle].IsDependentStyle(), + "Cannot get dependent builtin style"); + return &gBuiltinStyleTable[aStyle]; +} + +bool +CounterStyleManager::NotifyRuleChanged() +{ + bool changed = false; + nsTArray> kungFuDeathGrip; + for (auto iter = mCacheTable.Iter(); !iter.Done(); iter.Next()) { + RefPtr& style = iter.Data(); + bool toBeUpdated = false; + bool toBeRemoved = false; + // XXXheycam ServoStyleSets do not support custom counter styles yet. + StyleSetHandle styleSet = mPresContext->StyleSet(); + NS_ASSERTION(styleSet->IsGecko(), + "stylo: ServoStyleSets do not support custom counter " + "styles yet"); + nsCSSCounterStyleRule* newRule = styleSet->IsGecko() ? + styleSet->AsGecko()->CounterStyleRuleForName(iter.Key()) : nullptr; + if (!newRule) { + if (style->IsCustomStyle()) { + toBeRemoved = true; + } + } else { + if (!style->IsCustomStyle()) { + toBeRemoved = true; + } else { + auto custom = static_cast(style.get()); + if (custom->GetRule() != newRule) { + toBeRemoved = true; + } else if (custom->GetRuleGeneration() != newRule->GetGeneration()) { + toBeUpdated = true; + custom->ResetCachedData(); + } + } + } + changed = changed || toBeUpdated || toBeRemoved; + if (toBeRemoved) { + if (style->IsDependentStyle()) { + if (style->IsCustomStyle()) { + // Since |style| is being removed from mCacheTable, it won't be + // visited by our post-removal iteration. So, we have to give it a + // manual ResetDependentData() call. (This only really matters if + // something else is holding a reference and keeping it alive.) + static_cast(style.get())->ResetDependentData(); + } + // The object has to be held here so that it will not be released + // before all pointers that refer to it are reset. It will be released + // when kungFuDeathGrip goes out of scope at the end of this function. + kungFuDeathGrip.AppendElement(style); + } + iter.Remove(); + } + } + + if (changed) { + for (auto iter = mCacheTable.Iter(); !iter.Done(); iter.Next()) { + CounterStyle* style = iter.UserData(); + 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; +} + +} // namespace mozilla diff --git a/layout/style/CounterStyleManager.h b/layout/style/CounterStyleManager.h new file mode 100644 index 000000000..2f760f340 --- /dev/null +++ b/layout/style/CounterStyleManager.h @@ -0,0 +1,192 @@ +/* -*- 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 "nsStringFwd.h" +#include "nsRefPtrHashtable.h" +#include "nsHashKeys.h" + +#include "nsStyleConsts.h" + +#include "mozilla/Attributes.h" + +#include "nsCSSValue.h" + +class nsPresContext; + +namespace mozilla { + +class WritingMode; + +typedef int32_t CounterValue; + +class CounterStyleManager; +class AnonymousCounterStyle; + +struct NegativeType; +struct PadType; + +class CounterStyle +{ +protected: + explicit constexpr CounterStyle(int32_t aStyle) + : mStyle(aStyle) + { + } + +private: + CounterStyle(const CounterStyle& aOther) = delete; + void operator=(const CounterStyle& other) = delete; + +public: + int32_t GetStyle() const { return mStyle; } + bool IsNone() const { return mStyle == NS_STYLE_LIST_STYLE_NONE; } + bool IsCustomStyle() const { return mStyle == NS_STYLE_LIST_STYLE_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 GetStyleName(nsSubstring& aResult) = 0; + virtual void GetPrefix(nsSubstring& aResult) = 0; + virtual void GetSuffix(nsSubstring& aResult) = 0; + void GetCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL); + virtual void GetSpokenCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& 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 uint8_t GetSpeakAs() = 0; + virtual bool UseNegativeSign() = 0; + + virtual void CallFallbackStyle(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL); + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL) = 0; + + virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; } + + NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0; + NS_IMETHOD_(MozExternalRefCountType) Release() = 0; + +protected: + int32_t mStyle; +}; + +class AnonymousCounterStyle final : public CounterStyle +{ +public: + explicit AnonymousCounterStyle(const nsSubstring& aContent); + explicit AnonymousCounterStyle(const nsCSSValue::Array* aValue); + + virtual void GetStyleName(nsAString& aResult) override; + 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 uint8_t GetSpeakAs() override; + virtual bool UseNegativeSign() override; + + virtual bool GetInitialCounterText(CounterValue aOrdinal, + WritingMode aWritingMode, + nsSubstring& aResult, + bool& aIsRTL) override; + + virtual AnonymousCounterStyle* AsAnonymous() override { return this; } + + bool IsSingleString() const { return mSingleString; } + uint8_t GetSystem() const { return mSystem; } + const nsTArray& GetSymbols() const { return mSymbols; } + + NS_INLINE_DECL_REFCOUNTING(AnonymousCounterStyle, override) + +private: + ~AnonymousCounterStyle() {} + + bool mSingleString; + uint8_t mSystem; + nsTArray mSymbols; +}; + +class CounterStyleManager final +{ +private: + ~CounterStyleManager(); +public: + explicit CounterStyleManager(nsPresContext* aPresContext); + + static void InitializeBuiltinCounterStyles(); + + void Disconnect(); + + bool IsInitial() const + { + // only 'none' and 'decimal' + return mCacheTable.Count() == 2; + } + + CounterStyle* BuildCounterStyle(const nsSubstring& aName); + + static CounterStyle* GetBuiltinStyle(int32_t aStyle); + static CounterStyle* GetNoneStyle() + { + return GetBuiltinStyle(NS_STYLE_LIST_STYLE_NONE); + } + static CounterStyle* GetDecimalStyle() + { + return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DECIMAL); + } + + // 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(); + + nsPresContext* PresContext() const { return mPresContext; } + + NS_INLINE_DECL_REFCOUNTING(CounterStyleManager) + +private: + nsPresContext* mPresContext; + nsRefPtrHashtable mCacheTable; +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_CounterStyleManager_h_) */ diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp new file mode 100644 index 000000000..c67f6b2a2 --- /dev/null +++ b/layout/style/Declaration.cpp @@ -0,0 +1,1988 @@ +/* -*- 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/. */ + +/* + * representation of a declaration block (or style attribute) in a CSS + * stylesheet + */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/MemoryReporting.h" + +#include "mozilla/css/Declaration.h" +#include "nsPrintfCString.h" +#include "gfxFontConstants.h" +#include "nsStyleUtil.h" + +namespace mozilla { +namespace css { + +NS_IMPL_QUERY_INTERFACE(ImportantStyleData, nsIStyleRule) +NS_IMPL_ADDREF_USING_AGGREGATOR(ImportantStyleData, Declaration()) +NS_IMPL_RELEASE_USING_AGGREGATOR(ImportantStyleData, Declaration()) + +/* virtual */ void +ImportantStyleData::MapRuleInfoInto(nsRuleData* aRuleData) +{ + Declaration()->MapImportantRuleInfoInto(aRuleData); +} + +/* virtual */ bool +ImportantStyleData::MightMapInheritedStyleData() +{ + return Declaration()->MapsImportantInheritedStyleData(); +} + +/* virtual */ bool +ImportantStyleData::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) +{ + return Declaration()->GetDiscretelyAnimatedCSSValue(aProperty, aValue); +} + + +#ifdef DEBUG +/* virtual */ void +ImportantStyleData::List(FILE* out, int32_t aIndent) const +{ + // Indent + nsAutoCString str; + for (int32_t index = aIndent; --index >= 0; ) { + str.AppendLiteral(" "); + } + + str.AppendLiteral("! important rule\n"); + fprintf_stderr(out, "%s", str.get()); +} +#endif + +Declaration::Declaration(const Declaration& aCopy) + : DeclarationBlock(aCopy), + mOrder(aCopy.mOrder), + mVariableOrder(aCopy.mVariableOrder), + mData(aCopy.mData ? aCopy.mData->Clone() : nullptr), + mImportantData(aCopy.mImportantData ? + aCopy.mImportantData->Clone() : nullptr), + mVariables(aCopy.mVariables ? + new CSSVariableDeclarations(*aCopy.mVariables) : + nullptr), + mImportantVariables(aCopy.mImportantVariables ? + new CSSVariableDeclarations(*aCopy.mImportantVariables) : + nullptr) +{ +} + +Declaration::~Declaration() +{ +} + +NS_INTERFACE_MAP_BEGIN(Declaration) + if (aIID.Equals(NS_GET_IID(mozilla::css::Declaration))) { + *aInstancePtr = this; + NS_ADDREF_THIS(); + return NS_OK; + } + else + NS_INTERFACE_MAP_ENTRY(nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(Declaration) +NS_IMPL_RELEASE(Declaration) + +/* virtual */ void +Declaration::MapRuleInfoInto(nsRuleData* aRuleData) +{ + MOZ_ASSERT(mData, "must call only while compressed"); + mData->MapRuleInfoInto(aRuleData); + if (mVariables) { + mVariables->MapRuleInfoInto(aRuleData); + } +} + +/* virtual */ bool +Declaration::MightMapInheritedStyleData() +{ + MOZ_ASSERT(mData, "must call only while compressed"); + if (mVariables && mVariables->Count() != 0) { + return true; + } + return mData->HasInheritedStyleData(); +} + +/* virtual */ bool +Declaration::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) +{ + nsCSSCompressedDataBlock* data = GetPropertyIsImportantByID(aProperty) + ? mImportantData : mData; + const nsCSSValue* value = data->ValueFor(aProperty); + if (!value) { + return false; + } + *aValue = *value; + return true; +} + + +bool +Declaration::MapsImportantInheritedStyleData() const +{ + MOZ_ASSERT(mData, "must call only while compressed"); + MOZ_ASSERT(HasImportantData(), "must only be called for Declarations with " + "important data"); + if (mImportantVariables && mImportantVariables->Count() != 0) { + return true; + } + return mImportantData ? mImportantData->HasInheritedStyleData() : false; +} + +void +Declaration::ValueAppended(nsCSSPropertyID aProperty) +{ + MOZ_ASSERT(!mData && !mImportantData, + "should only be called while expanded"); + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), + "shorthands forbidden"); + // order IS important for CSS, so remove and add to the end + mOrder.RemoveElement(static_cast(aProperty)); + mOrder.AppendElement(static_cast(aProperty)); +} + +template +inline void +DispatchPropertyOperation(const nsAString& aProperty, + PropFunc aPropFunc, CustomPropFunc aCustomPropFunc) +{ + nsCSSPropertyID propID = + nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent); + if (propID != eCSSProperty_UNKNOWN) { + if (propID != eCSSPropertyExtra_variable) { + aPropFunc(propID); + } else { + aCustomPropFunc(Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH)); + } + } +} + +void +Declaration::GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { GetPropertyValueByID(propID, aValue); }, + [&](const nsAString& name) { GetVariableValue(name, aValue); }); +} + +void +Declaration::GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const +{ + GetPropertyValueInternal(aPropID, aValue, nsCSSValue::eNormalized); +} + +void +Declaration::GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { + GetPropertyValueInternal(propID, aValue, nsCSSValue::eAuthorSpecified); + }, + [&](const nsAString& name) { GetVariableValue(name, aValue); }); +} + +bool +Declaration::GetPropertyIsImportant(const nsAString& aProperty) const +{ + bool r = false; + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { r = GetPropertyIsImportantByID(propID); }, + [&](const nsAString& name) { r = GetVariableIsImportant(name); }); + return r; +} + +void +Declaration::RemoveProperty(const nsAString& aProperty) +{ + DispatchPropertyOperation(aProperty, + [&](nsCSSPropertyID propID) { RemovePropertyByID(propID); }, + [&](const nsAString& name) { RemoveVariable(name); }); +} + +void +Declaration::RemovePropertyByID(nsCSSPropertyID aProperty) +{ + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT); + + nsCSSExpandedDataBlock data; + ExpandTo(&data); + MOZ_ASSERT(!mData && !mImportantData, "Expand didn't null things out"); + + if (nsCSSProps::IsShorthand(aProperty)) { + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, + CSSEnabledState::eForAllContent) { + data.ClearLonghandProperty(*p); + mOrder.RemoveElement(static_cast(*p)); + } + } else { + data.ClearLonghandProperty(aProperty); + mOrder.RemoveElement(static_cast(aProperty)); + } + + CompressFrom(&data); +} + +bool +Declaration::HasProperty(nsCSSPropertyID aProperty) const +{ + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "property ID out of range"); + + nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty) + ? mImportantData : mData; + const nsCSSValue *val = data->ValueFor(aProperty); + return !!val; +} + +bool +Declaration::AppendValueToString(nsCSSPropertyID aProperty, + nsAString& aResult, + nsCSSValue::Serialization aSerialization) const +{ + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "property ID out of range"); + + nsCSSCompressedDataBlock *data = GetPropertyIsImportantByID(aProperty) + ? mImportantData : mData; + const nsCSSValue *val = data->ValueFor(aProperty); + if (!val) { + return false; + } + + val->AppendToString(aProperty, aResult, aSerialization); + return true; +} + +static void +AppendSingleImageLayerPositionValue(const nsCSSValue& aPositionX, + const nsCSSValue& aPositionY, + const nsCSSPropertyID aTable[], + nsAString& aValue, + nsCSSValue::Serialization aSerialization) +{ + // We need to make sure that we don't serialize to an invalid 3-value form. + // The 3-value form is only valid if both edges are present. + const nsCSSValue &xEdge = aPositionX.GetArrayValue()->Item(0); + const nsCSSValue &xOffset = aPositionX.GetArrayValue()->Item(1); + const nsCSSValue &yEdge = aPositionY.GetArrayValue()->Item(0); + const nsCSSValue &yOffset = aPositionY.GetArrayValue()->Item(1); + bool xHasEdge = (eCSSUnit_Enumerated == xEdge.GetUnit()); + bool xHasBoth = xHasEdge && (eCSSUnit_Null != xOffset.GetUnit()); + bool yHasEdge = (eCSSUnit_Enumerated == yEdge.GetUnit()); + bool yHasBoth = yHasEdge && (eCSSUnit_Null != yOffset.GetUnit()); + + if (yHasBoth && !xHasEdge) { + // Output 4-value form by adding the x edge. + aValue.AppendLiteral("left "); + } + aPositionX.AppendToString(aTable[nsStyleImageLayers::positionX], + aValue, aSerialization); + + aValue.Append(char16_t(' ')); + + if (xHasBoth && !yHasEdge) { + // Output 4-value form by adding the y edge. + aValue.AppendLiteral("top "); + } + aPositionY.AppendToString(aTable[nsStyleImageLayers::positionY], + aValue, aSerialization); +} + +void +Declaration::GetImageLayerValue( + nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSPropertyID aTable[]) const +{ + // We know from our caller that all subproperties were specified. + // However, we still can't represent that in the shorthand unless + // they're all lists of the same length. So if they're different + // lengths, we need to bail out. + // We also need to bail out if an item has background-clip and + // background-origin that are different and not the default + // values. (We omit them if they're both default.) + + // Common CSS properties for both background & mask layer. + const nsCSSValueList *image = + data->ValueFor(aTable[nsStyleImageLayers::image])->GetListValue(); + const nsCSSValuePairList *repeat = + data->ValueFor(aTable[nsStyleImageLayers::repeat])->GetPairListValue(); + const nsCSSValueList *positionX = + data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue(); + const nsCSSValueList *positionY = + data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue(); + const nsCSSValueList *clip = + data->ValueFor(aTable[nsStyleImageLayers::clip])->GetListValue(); + const nsCSSValueList *origin = + data->ValueFor(aTable[nsStyleImageLayers::origin])->GetListValue(); + const nsCSSValuePairList *size = + data->ValueFor(aTable[nsStyleImageLayers::size])->GetPairListValue(); + + // Background layer property. + const nsCSSValueList *attachment = + (aTable[nsStyleImageLayers::attachment] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::attachment])->GetListValue(); + + // Mask layer properties. + const nsCSSValueList *composite = + (aTable[nsStyleImageLayers::composite] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::composite])->GetListValue(); + const nsCSSValueList *mode = + (aTable[nsStyleImageLayers::maskMode] == eCSSProperty_UNKNOWN)? + nullptr : + data->ValueFor(aTable[nsStyleImageLayers::maskMode])->GetListValue(); + + for (;;) { + // Serialize background-color at the beginning of the last item. + if (!image->mNext) { + if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + AppendValueToString(aTable[nsStyleImageLayers::color], aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + } + + image->mValue.AppendToString(aTable[nsStyleImageLayers::image], aValue, + aSerialization); + + aValue.Append(char16_t(' ')); + repeat->mXValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue, + aSerialization); + if (repeat->mYValue.GetUnit() != eCSSUnit_Null) { + repeat->mYValue.AppendToString(aTable[nsStyleImageLayers::repeat], aValue, + aSerialization); + } + + if (attachment) { + aValue.Append(char16_t(' ')); + attachment->mValue.AppendToString(aTable[nsStyleImageLayers::attachment], + aValue, aSerialization); + } + + aValue.Append(char16_t(' ')); + AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue, + aTable, aValue, aSerialization); + + if (size->mXValue.GetUnit() != eCSSUnit_Auto || + size->mYValue.GetUnit() != eCSSUnit_Auto) { + aValue.Append(char16_t(' ')); + aValue.Append(char16_t('/')); + aValue.Append(char16_t(' ')); + size->mXValue.AppendToString(aTable[nsStyleImageLayers::size], aValue, + aSerialization); + aValue.Append(char16_t(' ')); + size->mYValue.AppendToString(aTable[nsStyleImageLayers::size], aValue, + aSerialization); + } + + MOZ_ASSERT(clip->mValue.GetUnit() == eCSSUnit_Enumerated && + origin->mValue.GetUnit() == eCSSUnit_Enumerated, + "should not have inherit/initial within list"); + + int32_t originDefaultValue = + (aTable == nsStyleImageLayers::kBackgroundLayerTable) + ? NS_STYLE_IMAGELAYER_ORIGIN_PADDING : NS_STYLE_IMAGELAYER_ORIGIN_BORDER; + if (clip->mValue.GetIntValue() != NS_STYLE_IMAGELAYER_CLIP_BORDER || + origin->mValue.GetIntValue() != originDefaultValue) { +#ifdef DEBUG + for (size_t i = 0; nsCSSProps::kImageLayerOriginKTable[i].mValue != -1; i++) { + // For each keyword & value in kOriginKTable, ensure that + // kBackgroundKTable has a matching entry at the same position. + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mKeyword == + nsCSSProps::kBackgroundClipKTable[i].mKeyword); + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mValue == + nsCSSProps::kBackgroundClipKTable[i].mValue); + } +#endif + static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == + NS_STYLE_IMAGELAYER_ORIGIN_BORDER && + NS_STYLE_IMAGELAYER_CLIP_PADDING == + NS_STYLE_IMAGELAYER_ORIGIN_PADDING && + NS_STYLE_IMAGELAYER_CLIP_CONTENT == + NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, + "mask-clip and mask-origin style constants must agree"); + aValue.Append(char16_t(' ')); + origin->mValue.AppendToString(aTable[nsStyleImageLayers::origin], aValue, + aSerialization); + + if (clip->mValue != origin->mValue) { + aValue.Append(char16_t(' ')); + clip->mValue.AppendToString(aTable[nsStyleImageLayers::clip], aValue, + aSerialization); + } + } + + if (composite) { + aValue.Append(char16_t(' ')); + composite->mValue.AppendToString(aTable[nsStyleImageLayers::composite], + aValue, aSerialization); + } + + if (mode) { + aValue.Append(char16_t(' ')); + mode->mValue.AppendToString(aTable[nsStyleImageLayers::maskMode], + aValue, aSerialization); + } + + image = image->mNext; + repeat = repeat->mNext; + positionX = positionX->mNext; + positionY = positionY->mNext; + clip = clip->mNext; + origin = origin->mNext; + size = size->mNext; + attachment = attachment ? attachment->mNext : nullptr; + composite = composite ? composite->mNext : nullptr; + mode = mode ? mode->mNext : nullptr; + + if (!image) { + // This layer is an background layer + if (aTable == nsStyleImageLayers::kBackgroundLayerTable) { + if (repeat || positionX || positionY || clip || origin || size || + attachment) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + // This layer is an mask layer + } else { +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable); +#else + MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable"); +#endif + if (repeat || positionX || positionY || clip || origin || size || + composite || mode) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + } + break; + } + + // This layer is an background layer + if (aTable == nsStyleImageLayers::kBackgroundLayerTable) { + if (!repeat || !positionX || !positionY || !clip || !origin || !size || + !attachment) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + // This layer is an mask layer + } else { +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable); +#else + MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable"); +#endif + if (!repeat || !positionX || !positionY || !clip || !origin || !size || + !composite || !mode) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + return; + } + } + aValue.Append(char16_t(',')); + aValue.Append(char16_t(' ')); + } +} + +void +Declaration::GetImageLayerPositionValue( + nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSPropertyID aTable[]) const +{ + // We know from above that all subproperties were specified. + // However, we still can't represent that in the shorthand unless + // they're all lists of the same length. So if they're different + // lengths, we need to bail out. + const nsCSSValueList *positionX = + data->ValueFor(aTable[nsStyleImageLayers::positionX])->GetListValue(); + const nsCSSValueList *positionY = + data->ValueFor(aTable[nsStyleImageLayers::positionY])->GetListValue(); + for (;;) { + AppendSingleImageLayerPositionValue(positionX->mValue, positionY->mValue, + aTable, aValue, aSerialization); + positionX = positionX->mNext; + positionY = positionY->mNext; + + if (!positionX || !positionY) { + if (positionX || positionY) { + // Uneven length lists, so can't be serialized as shorthand. + aValue.Truncate(); + } + return; + } + aValue.Append(char16_t(',')); + aValue.Append(char16_t(' ')); + } +} + +void +Declaration::GetPropertyValueInternal( + nsCSSPropertyID aProperty, nsAString& aValue, + nsCSSValue::Serialization aSerialization) const +{ + aValue.Truncate(0); + + // simple properties are easy. + if (!nsCSSProps::IsShorthand(aProperty)) { + AppendValueToString(aProperty, aValue, aSerialization); + return; + } + + // DOM Level 2 Style says (when describing CSS2Properties, although + // not CSSStyleDeclaration.getPropertyValue): + // However, if there is no shorthand declaration that could be added + // to the ruleset without changing in any way the rules already + // declared in the ruleset (i.e., by adding longhand rules that were + // previously not declared in the ruleset), then the empty string + // should be returned for the shorthand property. + // This means we need to check a number of cases: + // (1) Since a shorthand sets all sub-properties, if some of its + // subproperties were not specified, we must return the empty + // string. + // (2) Since 'inherit', 'initial' and 'unset' can only be specified + // as the values for entire properties, we need to return the + // empty string if some but not all of the subproperties have one + // of those values. + // (3) Since a single value only makes sense with or without + // !important, we return the empty string if some values are + // !important and some are not. + // Since we're doing this check for 'inherit' and 'initial' up front, + // we can also simplify the property serialization code by serializing + // those values up front as well. + // + // Additionally, if a shorthand property was set using a value with a + // variable reference and none of its component longhand properties were + // then overridden on the declaration, we return the token stream + // assigned to the shorthand. + const nsCSSValue* tokenStream = nullptr; + uint32_t totalCount = 0, importantCount = 0, + initialCount = 0, inheritCount = 0, unsetCount = 0, + matchingTokenStreamCount = 0, nonMatchingTokenStreamCount = 0; + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, + CSSEnabledState::eForAllContent) { + if (*p == eCSSProperty__x_system_font) { + // The system-font subproperty doesn't count. + continue; + } + ++totalCount; + const nsCSSValue *val = mData->ValueFor(*p); + MOZ_ASSERT(!val || !mImportantData || !mImportantData->ValueFor(*p), + "can't be in both blocks"); + if (!val && mImportantData) { + ++importantCount; + val = mImportantData->ValueFor(*p); + } + if (!val) { + // Case (1) above: some subproperties not specified. + return; + } + if (val->GetUnit() == eCSSUnit_Inherit) { + ++inheritCount; + } else if (val->GetUnit() == eCSSUnit_Initial) { + ++initialCount; + } else if (val->GetUnit() == eCSSUnit_Unset) { + ++unsetCount; + } else if (val->GetUnit() == eCSSUnit_TokenStream) { + if (val->GetTokenStreamValue()->mShorthandPropertyID == aProperty) { + tokenStream = val; + ++matchingTokenStreamCount; + } else { + ++nonMatchingTokenStreamCount; + } + } + } + if (importantCount != 0 && importantCount != totalCount) { + // Case (3), no consistent importance. + return; + } + if (initialCount == totalCount) { + // Simplify serialization below by serializing initial up-front. + nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue, + nsCSSValue::eNormalized); + return; + } + if (inheritCount == totalCount) { + // Simplify serialization below by serializing inherit up-front. + nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue, + nsCSSValue::eNormalized); + return; + } + if (unsetCount == totalCount) { + // Simplify serialization below by serializing unset up-front. + nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue, + nsCSSValue::eNormalized); + return; + } + if (initialCount != 0 || inheritCount != 0 || + unsetCount != 0 || nonMatchingTokenStreamCount != 0) { + // Case (2): partially initial, inherit, unset or token stream. + return; + } + if (tokenStream) { + if (matchingTokenStreamCount == totalCount) { + // Shorthand was specified using variable references and all of its + // longhand components were set by the shorthand. + aValue.Append(tokenStream->GetTokenStreamValue()->mTokenStream); + } else { + // In all other cases, serialize to the empty string. + } + return; + } + + nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData; + switch (aProperty) { + case eCSSProperty_margin: + case eCSSProperty_padding: + case eCSSProperty_border_color: + case eCSSProperty_border_style: + case eCSSProperty_border_width: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[0]).Find("-top") != + kNotFound, "first subprop must be top"); + MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[1]).Find("-right") != + kNotFound, "second subprop must be right"); + MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[2]).Find("-bottom") != + kNotFound, "third subprop must be bottom"); + MOZ_ASSERT(nsCSSProps::GetStringValue(subprops[3]).Find("-left") != + kNotFound, "fourth subprop must be left"); + const nsCSSValue* vals[4] = { + data->ValueFor(subprops[0]), + data->ValueFor(subprops[1]), + data->ValueFor(subprops[2]), + data->ValueFor(subprops[3]) + }; + nsCSSValue::AppendSidesShorthandToString(subprops, vals, aValue, + aSerialization); + break; + } + case eCSSProperty_border_radius: + case eCSSProperty__moz_outline_radius: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + const nsCSSValue* vals[4] = { + data->ValueFor(subprops[0]), + data->ValueFor(subprops[1]), + data->ValueFor(subprops[2]), + data->ValueFor(subprops[3]) + }; + nsCSSValue::AppendBasicShapeRadiusToString(subprops, vals, aValue, + aSerialization); + break; + } + case eCSSProperty_border_image: { + // Even though there are some cases where we could omit + // 'border-image-source' (when it's none), it's probably not a + // good idea since it's likely to be confusing. It would also + // require adding the extra check that we serialize *something*. + AppendValueToString(eCSSProperty_border_image_source, aValue, + aSerialization); + + bool sliceDefault = data->HasDefaultBorderImageSlice(); + bool widthDefault = data->HasDefaultBorderImageWidth(); + bool outsetDefault = data->HasDefaultBorderImageOutset(); + + if (!sliceDefault || !widthDefault || !outsetDefault) { + aValue.Append(char16_t(' ')); + AppendValueToString(eCSSProperty_border_image_slice, aValue, + aSerialization); + if (!widthDefault || !outsetDefault) { + aValue.AppendLiteral(" /"); + if (!widthDefault) { + aValue.Append(char16_t(' ')); + AppendValueToString(eCSSProperty_border_image_width, aValue, + aSerialization); + } + if (!outsetDefault) { + aValue.AppendLiteral(" / "); + AppendValueToString(eCSSProperty_border_image_outset, aValue, + aSerialization); + } + } + } + + bool repeatDefault = data->HasDefaultBorderImageRepeat(); + if (!repeatDefault) { + aValue.Append(char16_t(' ')); + AppendValueToString(eCSSProperty_border_image_repeat, aValue, + aSerialization); + } + break; + } + case eCSSProperty_border: { + // If we have a non-default value for any of the properties that + // this shorthand sets but cannot specify, we have to return the + // empty string. + if (data->ValueFor(eCSSProperty_border_image_source)->GetUnit() != + eCSSUnit_None || + !data->HasDefaultBorderImageSlice() || + !data->HasDefaultBorderImageWidth() || + !data->HasDefaultBorderImageOutset() || + !data->HasDefaultBorderImageRepeat() || + data->ValueFor(eCSSProperty_border_top_colors)->GetUnit() != + eCSSUnit_None || + data->ValueFor(eCSSProperty_border_right_colors)->GetUnit() != + eCSSUnit_None || + data->ValueFor(eCSSProperty_border_bottom_colors)->GetUnit() != + eCSSUnit_None || + data->ValueFor(eCSSProperty_border_left_colors)->GetUnit() != + eCSSUnit_None) { + break; + } + + const nsCSSPropertyID* subproptables[3] = { + nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color), + nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style), + nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width) + }; + bool match = true; + for (const nsCSSPropertyID** subprops = subproptables, + **subprops_end = ArrayEnd(subproptables); + subprops < subprops_end; ++subprops) { + const nsCSSValue *firstSide = data->ValueFor((*subprops)[0]); + for (int32_t side = 1; side < 4; ++side) { + const nsCSSValue *otherSide = + data->ValueFor((*subprops)[side]); + if (*firstSide != *otherSide) + match = false; + } + } + if (!match) { + // We can't express what we have in the border shorthand + break; + } + // tweak aProperty and fall through + aProperty = eCSSProperty_border_top; + MOZ_FALLTHROUGH; + } + case eCSSProperty_border_top: + case eCSSProperty_border_right: + case eCSSProperty_border_bottom: + case eCSSProperty_border_left: + case eCSSProperty_border_inline_start: + case eCSSProperty_border_inline_end: + case eCSSProperty_border_block_start: + case eCSSProperty_border_block_end: + case eCSSProperty_column_rule: + case eCSSProperty_outline: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(StringEndsWith(nsCSSProps::GetStringValue(subprops[2]), + NS_LITERAL_CSTRING("-color")), + "third subprop must be the color property"); + const nsCSSValue *colorValue = data->ValueFor(subprops[2]); + bool isCurrentColor = + colorValue->GetUnit() == eCSSUnit_EnumColor && + colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR; + if (!AppendValueToString(subprops[0], aValue, aSerialization) || + !(aValue.Append(char16_t(' ')), + AppendValueToString(subprops[1], aValue, aSerialization)) || + // Don't output a third value when it's currentcolor. + !(isCurrentColor || + (aValue.Append(char16_t(' ')), + AppendValueToString(subprops[2], aValue, aSerialization)))) { + aValue.Truncate(); + } + break; + } + case eCSSProperty_background: { + GetImageLayerValue(data, aValue, aSerialization, + nsStyleImageLayers::kBackgroundLayerTable); + break; + } + case eCSSProperty_background_position: { + GetImageLayerPositionValue(data, aValue, aSerialization, + nsStyleImageLayers::kBackgroundLayerTable); + break; + } +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + case eCSSProperty_mask: { + GetImageLayerValue(data, aValue, aSerialization, + nsStyleImageLayers::kMaskLayerTable); + break; + } + case eCSSProperty_mask_position: { + GetImageLayerPositionValue(data, aValue, aSerialization, + nsStyleImageLayers::kMaskLayerTable); + break; + } +#endif + case eCSSProperty_font: { + // systemFont might not be present; other values are guaranteed to be + // available based on the shorthand check at the beginning of the + // function, as long as the prop is enabled + const nsCSSValue *systemFont = + data->ValueFor(eCSSProperty__x_system_font); + const nsCSSValue *style = + data->ValueFor(eCSSProperty_font_style); + const nsCSSValue *weight = + data->ValueFor(eCSSProperty_font_weight); + const nsCSSValue *size = + data->ValueFor(eCSSProperty_font_size); + const nsCSSValue *lh = + data->ValueFor(eCSSProperty_line_height); + const nsCSSValue *family = + data->ValueFor(eCSSProperty_font_family); + const nsCSSValue *stretch = + data->ValueFor(eCSSProperty_font_stretch); + const nsCSSValue *sizeAdjust = + data->ValueFor(eCSSProperty_font_size_adjust); + const nsCSSValue *featureSettings = + data->ValueFor(eCSSProperty_font_feature_settings); + const nsCSSValue *languageOverride = + data->ValueFor(eCSSProperty_font_language_override); + const nsCSSValue *fontKerning = + data->ValueFor(eCSSProperty_font_kerning); + const nsCSSValue *fontSynthesis = + data->ValueFor(eCSSProperty_font_synthesis); + const nsCSSValue *fontVariantAlternates = + data->ValueFor(eCSSProperty_font_variant_alternates); + const nsCSSValue *fontVariantCaps = + data->ValueFor(eCSSProperty_font_variant_caps); + const nsCSSValue *fontVariantEastAsian = + data->ValueFor(eCSSProperty_font_variant_east_asian); + const nsCSSValue *fontVariantLigatures = + data->ValueFor(eCSSProperty_font_variant_ligatures); + const nsCSSValue *fontVariantNumeric = + data->ValueFor(eCSSProperty_font_variant_numeric); + const nsCSSValue *fontVariantPosition = + data->ValueFor(eCSSProperty_font_variant_position); + + if (systemFont && + systemFont->GetUnit() != eCSSUnit_None && + systemFont->GetUnit() != eCSSUnit_Null) { + if (style->GetUnit() != eCSSUnit_System_Font || + weight->GetUnit() != eCSSUnit_System_Font || + size->GetUnit() != eCSSUnit_System_Font || + lh->GetUnit() != eCSSUnit_System_Font || + family->GetUnit() != eCSSUnit_System_Font || + stretch->GetUnit() != eCSSUnit_System_Font || + sizeAdjust->GetUnit() != eCSSUnit_System_Font || + featureSettings->GetUnit() != eCSSUnit_System_Font || + languageOverride->GetUnit() != eCSSUnit_System_Font || + fontKerning->GetUnit() != eCSSUnit_System_Font || + fontSynthesis->GetUnit() != eCSSUnit_System_Font || + fontVariantAlternates->GetUnit() != eCSSUnit_System_Font || + fontVariantCaps->GetUnit() != eCSSUnit_System_Font || + fontVariantEastAsian->GetUnit() != eCSSUnit_System_Font || + fontVariantLigatures->GetUnit() != eCSSUnit_System_Font || + fontVariantNumeric->GetUnit() != eCSSUnit_System_Font || + fontVariantPosition->GetUnit() != eCSSUnit_System_Font) { + // This can't be represented as a shorthand. + return; + } + systemFont->AppendToString(eCSSProperty__x_system_font, aValue, + aSerialization); + } else { + // properties reset by this shorthand property to their + // initial values but not represented in its syntax + if (sizeAdjust->GetUnit() != eCSSUnit_None || + featureSettings->GetUnit() != eCSSUnit_Normal || + languageOverride->GetUnit() != eCSSUnit_Normal || + fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO || + fontSynthesis->GetUnit() != eCSSUnit_Enumerated || + fontSynthesis->GetIntValue() != + (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) || + fontVariantAlternates->GetUnit() != eCSSUnit_Normal || + fontVariantEastAsian->GetUnit() != eCSSUnit_Normal || + fontVariantLigatures->GetUnit() != eCSSUnit_Normal || + fontVariantNumeric->GetUnit() != eCSSUnit_Normal || + fontVariantPosition->GetUnit() != eCSSUnit_Normal) { + return; + } + + // only a normal or small-caps values of font-variant-caps can + // be represented in the font shorthand + if (fontVariantCaps->GetUnit() != eCSSUnit_Normal && + (fontVariantCaps->GetUnit() != eCSSUnit_Enumerated || + fontVariantCaps->GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS)) { + return; + } + + if (style->GetUnit() != eCSSUnit_Enumerated || + style->GetIntValue() != NS_FONT_STYLE_NORMAL) { + style->AppendToString(eCSSProperty_font_style, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + if (fontVariantCaps->GetUnit() != eCSSUnit_Normal) { + fontVariantCaps->AppendToString(eCSSProperty_font_variant_caps, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + if (weight->GetUnit() != eCSSUnit_Enumerated || + weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) { + weight->AppendToString(eCSSProperty_font_weight, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + if (stretch->GetUnit() != eCSSUnit_Enumerated || + stretch->GetIntValue() != NS_FONT_STRETCH_NORMAL) { + stretch->AppendToString(eCSSProperty_font_stretch, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } + size->AppendToString(eCSSProperty_font_size, aValue, aSerialization); + if (lh->GetUnit() != eCSSUnit_Normal) { + aValue.Append(char16_t('/')); + lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization); + } + aValue.Append(char16_t(' ')); + family->AppendToString(eCSSProperty_font_family, aValue, + aSerialization); + } + break; + } + case eCSSProperty_font_variant: { + const nsCSSPropertyID *subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + const nsCSSValue *fontVariantLigatures = + data->ValueFor(eCSSProperty_font_variant_ligatures); + + // all subproperty values normal? system font? + bool normalLigs = true, normalNonLigs = true, systemFont = true, + hasSystem = false; + for (const nsCSSPropertyID *sp = subprops; *sp != eCSSProperty_UNKNOWN; sp++) { + const nsCSSValue *spVal = data->ValueFor(*sp); + bool isNormal = (spVal->GetUnit() == eCSSUnit_Normal); + if (*sp == eCSSProperty_font_variant_ligatures) { + normalLigs = normalLigs && isNormal; + } else { + normalNonLigs = normalNonLigs && isNormal; + } + bool isSystem = (spVal->GetUnit() == eCSSUnit_System_Font); + systemFont = systemFont && isSystem; + hasSystem = hasSystem || isSystem; + } + + bool ligsNone = + fontVariantLigatures->GetUnit() == eCSSUnit_None; + + // normal, none, or system font ==> single value + if ((normalLigs && normalNonLigs) || + (normalNonLigs && ligsNone) || + systemFont) { + fontVariantLigatures->AppendToString(eCSSProperty_font_variant_ligatures, + aValue, + aSerialization); + } else if (ligsNone || hasSystem) { + // ligatures none but other values are non-normal ==> empty + // at least one but not all values are system font ==> empty + return; + } else { + // iterate over and append non-normal values + bool appendSpace = false; + for (const nsCSSPropertyID *sp = subprops; + *sp != eCSSProperty_UNKNOWN; sp++) { + const nsCSSValue *spVal = data->ValueFor(*sp); + if (spVal && spVal->GetUnit() != eCSSUnit_Normal) { + if (appendSpace) { + aValue.Append(char16_t(' ')); + } else { + appendSpace = true; + } + spVal->AppendToString(*sp, aValue, aSerialization); + } + } + } + break; + } + case eCSSProperty_list_style: + if (AppendValueToString(eCSSProperty_list_style_position, aValue, + aSerialization)) { + aValue.Append(char16_t(' ')); + } + if (AppendValueToString(eCSSProperty_list_style_image, aValue, + aSerialization)) { + aValue.Append(char16_t(' ')); + } + AppendValueToString(eCSSProperty_list_style_type, aValue, + aSerialization); + break; + case eCSSProperty_overflow: { + const nsCSSValue &xValue = + *data->ValueFor(eCSSProperty_overflow_x); + const nsCSSValue &yValue = + *data->ValueFor(eCSSProperty_overflow_y); + if (xValue == yValue) + xValue.AppendToString(eCSSProperty_overflow_x, aValue, aSerialization); + break; + } + case eCSSProperty_text_decoration: { + const nsCSSValue *decorationColor = + data->ValueFor(eCSSProperty_text_decoration_color); + const nsCSSValue *decorationStyle = + data->ValueFor(eCSSProperty_text_decoration_style); + + MOZ_ASSERT(decorationStyle->GetUnit() == eCSSUnit_Enumerated, + "bad text-decoration-style unit"); + + AppendValueToString(eCSSProperty_text_decoration_line, aValue, + aSerialization); + if (decorationStyle->GetIntValue() != + NS_STYLE_TEXT_DECORATION_STYLE_SOLID) { + aValue.Append(char16_t(' ')); + AppendValueToString(eCSSProperty_text_decoration_style, aValue, + aSerialization); + } + if (decorationColor->GetUnit() != eCSSUnit_EnumColor || + decorationColor->GetIntValue() != NS_COLOR_CURRENTCOLOR) { + aValue.Append(char16_t(' ')); + AppendValueToString(eCSSProperty_text_decoration_color, aValue, + aSerialization); + } + break; + } + case eCSSProperty_transition: { + const nsCSSValue *transProp = + data->ValueFor(eCSSProperty_transition_property); + const nsCSSValue *transDuration = + data->ValueFor(eCSSProperty_transition_duration); + const nsCSSValue *transTiming = + data->ValueFor(eCSSProperty_transition_timing_function); + const nsCSSValue *transDelay = + data->ValueFor(eCSSProperty_transition_delay); + + MOZ_ASSERT(transDuration->GetUnit() == eCSSUnit_List || + transDuration->GetUnit() == eCSSUnit_ListDep, + "bad t-duration unit"); + MOZ_ASSERT(transTiming->GetUnit() == eCSSUnit_List || + transTiming->GetUnit() == eCSSUnit_ListDep, + "bad t-timing unit"); + MOZ_ASSERT(transDelay->GetUnit() == eCSSUnit_List || + transDelay->GetUnit() == eCSSUnit_ListDep, + "bad t-delay unit"); + + const nsCSSValueList* dur = transDuration->GetListValue(); + const nsCSSValueList* tim = transTiming->GetListValue(); + const nsCSSValueList* del = transDelay->GetListValue(); + + if (transProp->GetUnit() == eCSSUnit_None || + transProp->GetUnit() == eCSSUnit_All) { + // If any of the other three lists has more than one element, + // we can't use the shorthand. + if (!dur->mNext && !tim->mNext && !del->mNext) { + transProp->AppendToString(eCSSProperty_transition_property, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + dur->mValue.AppendToString(eCSSProperty_transition_duration,aValue, + aSerialization); + aValue.Append(char16_t(' ')); + tim->mValue.AppendToString(eCSSProperty_transition_timing_function, + aValue, aSerialization); + aValue.Append(char16_t(' ')); + del->mValue.AppendToString(eCSSProperty_transition_delay, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } else { + aValue.Truncate(); + } + } else { + MOZ_ASSERT(transProp->GetUnit() == eCSSUnit_List || + transProp->GetUnit() == eCSSUnit_ListDep, + "bad t-prop unit"); + const nsCSSValueList* pro = transProp->GetListValue(); + for (;;) { + pro->mValue.AppendToString(eCSSProperty_transition_property, + aValue, aSerialization); + aValue.Append(char16_t(' ')); + dur->mValue.AppendToString(eCSSProperty_transition_duration, + aValue, aSerialization); + aValue.Append(char16_t(' ')); + tim->mValue.AppendToString(eCSSProperty_transition_timing_function, + aValue, aSerialization); + aValue.Append(char16_t(' ')); + del->mValue.AppendToString(eCSSProperty_transition_delay, + aValue, aSerialization); + pro = pro->mNext; + dur = dur->mNext; + tim = tim->mNext; + del = del->mNext; + if (!pro || !dur || !tim || !del) { + break; + } + aValue.AppendLiteral(", "); + } + if (pro || dur || tim || del) { + // Lists not all the same length, can't use shorthand. + aValue.Truncate(); + } + } + break; + } + case eCSSProperty_animation: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(eCSSProperty_animation); + static const size_t numProps = 8; + MOZ_ASSERT(subprops[numProps] == eCSSProperty_UNKNOWN, + "unexpected number of subproperties"); + const nsCSSValue* values[numProps]; + const nsCSSValueList* lists[numProps]; + + for (uint32_t i = 0; i < numProps; ++i) { + values[i] = data->ValueFor(subprops[i]); + MOZ_ASSERT(values[i]->GetUnit() == eCSSUnit_List || + values[i]->GetUnit() == eCSSUnit_ListDep, + "bad a-duration unit"); + lists[i] = values[i]->GetListValue(); + } + + for (;;) { + // We must serialize 'animation-name' last in case it has + // a value that conflicts with one of the other keyword properties. + MOZ_ASSERT(subprops[numProps - 1] == eCSSProperty_animation_name, + "animation-name must be last"); + bool done = false; + for (uint32_t i = 0;;) { + lists[i]->mValue.AppendToString(subprops[i], aValue, aSerialization); + lists[i] = lists[i]->mNext; + if (!lists[i]) { + done = true; + } + if (++i == numProps) { + break; + } + aValue.Append(char16_t(' ')); + } + if (done) { + break; + } + aValue.AppendLiteral(", "); + } + for (uint32_t i = 0; i < numProps; ++i) { + if (lists[i]) { + // Lists not all the same length, can't use shorthand. + aValue.Truncate(); + break; + } + } + break; + } + case eCSSProperty_marker: { + const nsCSSValue &endValue = + *data->ValueFor(eCSSProperty_marker_end); + const nsCSSValue &midValue = + *data->ValueFor(eCSSProperty_marker_mid); + const nsCSSValue &startValue = + *data->ValueFor(eCSSProperty_marker_start); + if (endValue == midValue && midValue == startValue) + AppendValueToString(eCSSProperty_marker_end, aValue, aSerialization); + break; + } + case eCSSProperty_columns: { + // Two values, column-count and column-width, separated by a space. + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + AppendValueToString(subprops[0], aValue, aSerialization); + aValue.Append(char16_t(' ')); + AppendValueToString(subprops[1], aValue, aSerialization); + break; + } + case eCSSProperty_flex: { + // flex-grow, flex-shrink, flex-basis, separated by single space + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + + AppendValueToString(subprops[0], aValue, aSerialization); + aValue.Append(char16_t(' ')); + AppendValueToString(subprops[1], aValue, aSerialization); + aValue.Append(char16_t(' ')); + AppendValueToString(subprops[2], aValue, aSerialization); + break; + } + case eCSSProperty_flex_flow: { + // flex-direction, flex-wrap, separated by single space + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN, + "must have exactly two subproperties"); + + AppendValueToString(subprops[0], aValue, aSerialization); + aValue.Append(char16_t(' ')); + AppendValueToString(subprops[1], aValue, aSerialization); + break; + } + case eCSSProperty_grid_row: + case eCSSProperty_grid_column: { + // grid-{row,column}-start, grid-{row,column}-end, separated by a slash + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN, + "must have exactly two subproperties"); + + // TODO: should we simplify when possible? + AppendValueToString(subprops[0], aValue, aSerialization); + aValue.AppendLiteral(" / "); + AppendValueToString(subprops[1], aValue, aSerialization); + break; + } + case eCSSProperty_grid_area: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[4] == eCSSProperty_UNKNOWN, + "must have exactly four subproperties"); + + // TODO: should we simplify when possible? + AppendValueToString(subprops[0], aValue, aSerialization); + aValue.AppendLiteral(" / "); + AppendValueToString(subprops[1], aValue, aSerialization); + aValue.AppendLiteral(" / "); + AppendValueToString(subprops[2], aValue, aSerialization); + aValue.AppendLiteral(" / "); + AppendValueToString(subprops[3], aValue, aSerialization); + break; + } + + // The 'grid' shorthand has 3 different possibilities for syntax: + // #1 <'grid-template'> + // #2 <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? + // #3 [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'> + case eCSSProperty_grid: { + const nsCSSValue& columnGapValue = + *data->ValueFor(eCSSProperty_grid_column_gap); + if (columnGapValue.GetUnit() != eCSSUnit_Pixel || + columnGapValue.GetFloatValue() != 0.0f) { + return; // Not serializable, bail. + } + const nsCSSValue& rowGapValue = + *data->ValueFor(eCSSProperty_grid_row_gap); + if (rowGapValue.GetUnit() != eCSSUnit_Pixel || + rowGapValue.GetFloatValue() != 0.0f) { + return; // Not serializable, bail. + } + const nsCSSValue& areasValue = + *data->ValueFor(eCSSProperty_grid_template_areas); + const nsCSSValue& columnsValue = + *data->ValueFor(eCSSProperty_grid_template_columns); + const nsCSSValue& rowsValue = + *data->ValueFor(eCSSProperty_grid_template_rows); + + const nsCSSValue& autoFlowValue = + *data->ValueFor(eCSSProperty_grid_auto_flow); + const nsCSSValue& autoColumnsValue = + *data->ValueFor(eCSSProperty_grid_auto_columns); + const nsCSSValue& autoRowsValue = + *data->ValueFor(eCSSProperty_grid_auto_rows); + + // grid-template-rows/areas:none + default grid-auto-columns + + // non-default row grid-auto-flow or grid-auto-rows. + // --> serialize as 'grid' syntax #3. + // (for default grid-auto-flow/rows we prefer to serialize to + // "none ['/' ...]" instead using syntax #2 or #1 below) + if (rowsValue.GetUnit() == eCSSUnit_None && + areasValue.GetUnit() == eCSSUnit_None && + autoColumnsValue.GetUnit() == eCSSUnit_Auto && + autoFlowValue.GetUnit() == eCSSUnit_Enumerated && + (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_ROW) && + (autoFlowValue.GetIntValue() != NS_STYLE_GRID_AUTO_FLOW_ROW || + autoRowsValue.GetUnit() != eCSSUnit_Auto)) { + aValue.AppendLiteral("auto-flow"); + if (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_DENSE) { + aValue.AppendLiteral(" dense"); + } + if (autoRowsValue.GetUnit() != eCSSUnit_Auto) { + aValue.Append(' '); + AppendValueToString(eCSSProperty_grid_auto_rows, + aValue, aSerialization); + } + aValue.AppendLiteral(" / "); + AppendValueToString(eCSSProperty_grid_template_columns, + aValue, aSerialization); + break; + } + + // grid-template-columns/areas:none + column grid-auto-flow + + // default grid-auto-rows. + // --> serialize as 'grid' syntax #2. + if (columnsValue.GetUnit() == eCSSUnit_None && + areasValue.GetUnit() == eCSSUnit_None && + autoRowsValue.GetUnit() == eCSSUnit_Auto && + autoFlowValue.GetUnit() == eCSSUnit_Enumerated && + (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_COLUMN)) { + AppendValueToString(eCSSProperty_grid_template_rows, + aValue, aSerialization); + aValue.AppendLiteral(" / auto-flow "); + if (autoFlowValue.GetIntValue() & NS_STYLE_GRID_AUTO_FLOW_DENSE) { + aValue.AppendLiteral("dense "); + } + AppendValueToString(eCSSProperty_grid_auto_columns, + aValue, aSerialization); + break; + } + + if (!(autoFlowValue.GetUnit() == eCSSUnit_Enumerated && + autoFlowValue.GetIntValue() == NS_STYLE_GRID_AUTO_FLOW_ROW && + autoColumnsValue.GetUnit() == eCSSUnit_Auto && + autoRowsValue.GetUnit() == eCSSUnit_Auto)) { + // Not serializable, bail. + return; + } + // Fall through to eCSSProperty_grid_template (syntax #1) + MOZ_FALLTHROUGH; + } + case eCSSProperty_grid_template: { + const nsCSSValue& areasValue = + *data->ValueFor(eCSSProperty_grid_template_areas); + const nsCSSValue& columnsValue = + *data->ValueFor(eCSSProperty_grid_template_columns); + const nsCSSValue& rowsValue = + *data->ValueFor(eCSSProperty_grid_template_rows); + if (areasValue.GetUnit() == eCSSUnit_None) { + AppendValueToString(eCSSProperty_grid_template_rows, + aValue, aSerialization); + aValue.AppendLiteral(" / "); + AppendValueToString(eCSSProperty_grid_template_columns, + aValue, aSerialization); + break; + } + if (columnsValue.GetUnit() == eCSSUnit_List || + columnsValue.GetUnit() == eCSSUnit_ListDep) { + const nsCSSValueList* columnsItem = columnsValue.GetListValue(); + if (columnsItem->mValue.GetUnit() == eCSSUnit_Enumerated && + columnsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) { + // We have "grid-template-areas:[something]; grid-template-columns:subgrid" + // which isn't a value that the shorthand can express. Bail. + return; + } + } + if (rowsValue.GetUnit() != eCSSUnit_List && + rowsValue.GetUnit() != eCSSUnit_ListDep) { + // We have "grid-template-areas:[something]; grid-template-rows:none" + // which isn't a value that the shorthand can express. Bail. + return; + } + const nsCSSValueList* rowsItem = rowsValue.GetListValue(); + if (rowsItem->mValue.GetUnit() == eCSSUnit_Enumerated && + rowsItem->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) { + // We have "grid-template-areas:[something]; grid-template-rows:subgrid" + // which isn't a value that the shorthand can express. Bail. + return; + } + const GridTemplateAreasValue* areas = areasValue.GetGridTemplateAreas(); + uint32_t nRowItems = 0; + while (rowsItem) { + nRowItems++; + rowsItem = rowsItem->mNext; + } + MOZ_ASSERT(nRowItems % 2 == 1, "expected an odd number of items"); + if ((nRowItems - 1) / 2 != areas->NRows()) { + // Not serializable, bail. + return; + } + rowsItem = rowsValue.GetListValue(); + uint32_t row = 0; + for (;;) { + bool addSpaceSeparator = true; + nsCSSUnit unit = rowsItem->mValue.GetUnit(); + + if (unit == eCSSUnit_Null) { + // Empty or omitted . Serializes to nothing. + addSpaceSeparator = false; // Avoid a double space. + + } else if (unit == eCSSUnit_List || unit == eCSSUnit_ListDep) { + // Non-empty + aValue.Append('['); + rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows, + aValue, aSerialization); + aValue.Append(']'); + + } else { + nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[row++], aValue); + aValue.Append(char16_t(' ')); + + // + if (unit == eCSSUnit_Pair) { + // 'repeat()' isn't allowed with non-default 'grid-template-areas'. + aValue.Truncate(); + return; + } + rowsItem->mValue.AppendToString(eCSSProperty_grid_template_rows, + aValue, aSerialization); + if (rowsItem->mNext && + rowsItem->mNext->mValue.GetUnit() == eCSSUnit_Null && + !rowsItem->mNext->mNext) { + // Break out of the loop early to avoid a trailing space. + break; + } + } + + rowsItem = rowsItem->mNext; + if (!rowsItem) { + break; + } + + if (addSpaceSeparator) { + aValue.Append(char16_t(' ')); + } + } + if (columnsValue.GetUnit() != eCSSUnit_None) { + const nsCSSValueList* colsItem = columnsValue.GetListValue(); + colsItem = colsItem->mNext; // first value is + for (; colsItem; colsItem = colsItem->mNext) { + if (colsItem->mValue.GetUnit() == eCSSUnit_Pair) { + // 'repeat()' isn't allowed with non-default 'grid-template-areas'. + aValue.Truncate(); + return; + } + colsItem = colsItem->mNext; // skip + } + aValue.AppendLiteral(" / "); + AppendValueToString(eCSSProperty_grid_template_columns, + aValue, aSerialization); + } + break; + } + case eCSSProperty_place_content: + case eCSSProperty_place_items: + case eCSSProperty_place_self: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN, + "must have exactly two subproperties"); + auto IsSingleValue = [] (const nsCSSValue& aValue) { + switch (aValue.GetUnit()) { + case eCSSUnit_Auto: + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + case eCSSUnit_Unset: + return true; + case eCSSUnit_Enumerated: + // return false if there is a fallback value or + return aValue.GetIntValue() <= NS_STYLE_JUSTIFY_SPACE_EVENLY; + default: + MOZ_ASSERT_UNREACHABLE("Unexpected unit for CSS Align property val"); + return false; + } + }; + // Each value must be a single value (i.e. no fallback value and no + // ), otherwise it can't be represented as a shorthand + // value. ('first|last baseline' counts as a single value) + const nsCSSValue* align = data->ValueFor(subprops[0]); + const nsCSSValue* justify = data->ValueFor(subprops[1]); + if (!align || !IsSingleValue(*align) || + !justify || !IsSingleValue(*justify)) { + return; // Not serializable, bail. + } + MOZ_FALLTHROUGH; + } + case eCSSProperty_grid_gap: { + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[2] == eCSSProperty_UNKNOWN, + "must have exactly two subproperties"); + + nsAutoString val1, val2; + AppendValueToString(subprops[0], val1, aSerialization); + AppendValueToString(subprops[1], val2, aSerialization); + if (val1 == val2) { + aValue.Append(val1); + } else { + aValue.Append(val1); + aValue.Append(' '); + aValue.Append(val2); + } + break; + } + case eCSSProperty_text_emphasis: { + const nsCSSValue* emphasisStyle = + data->ValueFor(eCSSProperty_text_emphasis_style); + const nsCSSValue* emphasisColor = + data->ValueFor(eCSSProperty_text_emphasis_color); + bool isDefaultColor = emphasisColor->GetUnit() == eCSSUnit_EnumColor && + emphasisColor->GetIntValue() == NS_COLOR_CURRENTCOLOR; + + if (emphasisStyle->GetUnit() != eCSSUnit_None || isDefaultColor) { + AppendValueToString(eCSSProperty_text_emphasis_style, + aValue, aSerialization); + if (!isDefaultColor) { + aValue.Append(char16_t(' ')); + } + } + if (!isDefaultColor) { + AppendValueToString(eCSSProperty_text_emphasis_color, + aValue, aSerialization); + } + break; + } + case eCSSProperty__moz_transform: { + // shorthands that are just aliases with different parsing rules + const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + MOZ_ASSERT(subprops[1] == eCSSProperty_UNKNOWN, + "must have exactly one subproperty"); + AppendValueToString(subprops[0], aValue, aSerialization); + break; + } + case eCSSProperty_scroll_snap_type: { + const nsCSSValue& xValue = + *data->ValueFor(eCSSProperty_scroll_snap_type_x); + const nsCSSValue& yValue = + *data->ValueFor(eCSSProperty_scroll_snap_type_y); + if (xValue == yValue) { + AppendValueToString(eCSSProperty_scroll_snap_type_x, aValue, + aSerialization); + } + // If scroll-snap-type-x and scroll-snap-type-y are not equal, + // we don't have a shorthand that can express. Bail. + break; + } + case eCSSProperty__webkit_text_stroke: { + const nsCSSValue* strokeWidth = + data->ValueFor(eCSSProperty__webkit_text_stroke_width); + const nsCSSValue* strokeColor = + data->ValueFor(eCSSProperty__webkit_text_stroke_color); + bool isDefaultColor = strokeColor->GetUnit() == eCSSUnit_EnumColor && + strokeColor->GetIntValue() == NS_COLOR_CURRENTCOLOR; + + if (strokeWidth->GetUnit() != eCSSUnit_Integer || + strokeWidth->GetIntValue() != 0 || isDefaultColor) { + AppendValueToString(eCSSProperty__webkit_text_stroke_width, + aValue, aSerialization); + if (!isDefaultColor) { + aValue.Append(char16_t(' ')); + } + } + if (!isDefaultColor) { + AppendValueToString(eCSSProperty__webkit_text_stroke_color, + aValue, aSerialization); + } + break; + } + case eCSSProperty_all: + // If we got here, then we didn't have all "inherit" or "initial" or + // "unset" values for all of the longhand property components of 'all'. + // There is no other possible value that is valid for all properties, + // so serialize as the empty string. + break; + default: + MOZ_ASSERT(false, "no other shorthands"); + break; + } +} + +bool +Declaration::GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const +{ + if (!mImportantData) + return false; + + // Calling ValueFor is inefficient, but we can assume '!important' is rare. + + if (!nsCSSProps::IsShorthand(aProperty)) { + return mImportantData->ValueFor(aProperty) != nullptr; + } + + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, + CSSEnabledState::eForAllContent) { + if (*p == eCSSProperty__x_system_font) { + // The system_font subproperty doesn't count. + continue; + } + if (!mImportantData->ValueFor(*p)) { + return false; + } + } + return true; +} + +void +Declaration::AppendPropertyAndValueToString(nsCSSPropertyID aProperty, + nsAutoString& aValue, + nsAString& aResult) const +{ + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "property enum out of range"); + MOZ_ASSERT((aProperty < eCSSProperty_COUNT_no_shorthands) == aValue.IsEmpty(), + "aValue should be given for shorthands but not longhands"); + AppendASCIItoUTF16(nsCSSProps::GetStringValue(aProperty), aResult); + aResult.AppendLiteral(": "); + if (aValue.IsEmpty()) + AppendValueToString(aProperty, aResult, nsCSSValue::eNormalized); + else + aResult.Append(aValue); + if (GetPropertyIsImportantByID(aProperty)) { + aResult.AppendLiteral(" ! important"); + } + aResult.AppendLiteral("; "); +} + +void +Declaration::AppendVariableAndValueToString(const nsAString& aName, + nsAString& aResult) const +{ + nsAutoString localName; + localName.AppendLiteral("--"); + localName.Append(aName); + nsStyleUtil::AppendEscapedCSSIdent(localName, aResult); + CSSVariableDeclarations::Type type; + nsString value; + bool important; + + if (mImportantVariables && mImportantVariables->Get(aName, type, value)) { + important = true; + } else { + MOZ_ASSERT(mVariables); + MOZ_ASSERT(mVariables->Has(aName)); + mVariables->Get(aName, type, value); + important = false; + } + + switch (type) { + case CSSVariableDeclarations::eTokenStream: + if (value.IsEmpty()) { + aResult.Append(':'); + } else { + aResult.AppendLiteral(": "); + aResult.Append(value); + } + break; + + case CSSVariableDeclarations::eInitial: + aResult.AppendLiteral("initial"); + break; + + case CSSVariableDeclarations::eInherit: + aResult.AppendLiteral("inherit"); + break; + + case CSSVariableDeclarations::eUnset: + aResult.AppendLiteral("unset"); + break; + + default: + MOZ_ASSERT(false, "unexpected variable value type"); + } + + if (important) { + aResult.AppendLiteral("! important"); + } + aResult.AppendLiteral("; "); +} + +void +Declaration::ToString(nsAString& aString) const +{ + // Someone cares about this declaration's contents, so don't let it + // change from under them. See e.g. bug 338679. + SetImmutable(); + + nsCSSCompressedDataBlock *systemFontData = + GetPropertyIsImportantByID(eCSSProperty__x_system_font) ? mImportantData + : mData; + const nsCSSValue *systemFont = + systemFontData->ValueFor(eCSSProperty__x_system_font); + const bool haveSystemFont = systemFont && + systemFont->GetUnit() != eCSSUnit_None && + systemFont->GetUnit() != eCSSUnit_Null; + bool didSystemFont = false; + + int32_t count = mOrder.Length(); + int32_t index; + AutoTArray shorthandsUsed; + for (index = 0; index < count; index++) { + nsCSSPropertyID property = GetPropertyAt(index); + + if (property == eCSSPropertyExtra_variable) { + uint32_t variableIndex = mOrder[index] - eCSSProperty_COUNT; + AppendVariableAndValueToString(mVariableOrder[variableIndex], aString); + continue; + } + + if (!nsCSSProps::IsEnabled(property, CSSEnabledState::eForAllContent)) { + continue; + } + bool doneProperty = false; + + // If we already used this property in a shorthand, skip it. + if (shorthandsUsed.Length() > 0) { + for (const nsCSSPropertyID *shorthands = + nsCSSProps::ShorthandsContaining(property); + *shorthands != eCSSProperty_UNKNOWN; ++shorthands) { + if (shorthandsUsed.Contains(*shorthands)) { + doneProperty = true; + break; + } + } + if (doneProperty) + continue; + } + + // Try to use this property in a shorthand. + nsAutoString value; + for (const nsCSSPropertyID *shorthands = + nsCSSProps::ShorthandsContaining(property); + *shorthands != eCSSProperty_UNKNOWN; ++shorthands) { + // ShorthandsContaining returns the shorthands in order from those + // that contain the most subproperties to those that contain the + // least, which is exactly the order we want to test them. + nsCSSPropertyID shorthand = *shorthands; + + GetPropertyValueByID(shorthand, value); + + // in the system font case, skip over font-variant shorthand, since all + // subproperties are already dealt with via the font shorthand + if (shorthand == eCSSProperty_font_variant && + value.EqualsLiteral("-moz-use-system-font")) { + continue; + } + + // If GetPropertyValueByID gives us a non-empty string back, we can + // use that value; otherwise it's not possible to use this shorthand. + if (!value.IsEmpty()) { + AppendPropertyAndValueToString(shorthand, value, aString); + shorthandsUsed.AppendElement(shorthand); + doneProperty = true; + break; + } + + if (shorthand == eCSSProperty_font) { + if (haveSystemFont && !didSystemFont) { + // Output the shorthand font declaration that we will + // partially override later. But don't add it to + // |shorthandsUsed|, since we will have to override it. + systemFont->AppendToString(eCSSProperty__x_system_font, value, + nsCSSValue::eNormalized); + AppendPropertyAndValueToString(eCSSProperty_font, value, aString); + value.Truncate(); + didSystemFont = true; + } + + // That we output the system font is enough for this property if: + // (1) it's the hidden system font subproperty (which either + // means we output it or we don't have it), or + // (2) its value is the hidden system font value and it matches + // the hidden system font subproperty in importance, and + // we output the system font subproperty. + const nsCSSValue *val = systemFontData->ValueFor(property); + if (property == eCSSProperty__x_system_font || + (haveSystemFont && val && val->GetUnit() == eCSSUnit_System_Font)) { + doneProperty = true; + break; + } + } + } + if (doneProperty) + continue; + + MOZ_ASSERT(value.IsEmpty(), "value should be empty now"); + AppendPropertyAndValueToString(property, value, aString); + } + if (! aString.IsEmpty()) { + // if the string is not empty, we have trailing whitespace we + // should remove + aString.Truncate(aString.Length() - 1); + } +} + +#ifdef DEBUG +/* virtual */ void +Declaration::List(FILE* out, int32_t aIndent) const +{ + const Rule* owningRule = GetOwningRule(); + if (owningRule) { + // More useful to print the selector and sheet URI too. + owningRule->List(out, aIndent); + return; + } + + nsAutoCString str; + for (int32_t index = aIndent; --index >= 0; ) { + str.AppendLiteral(" "); + } + + str.AppendLiteral("{ "); + nsAutoString s; + ToString(s); + AppendUTF16toUTF8(s, str); + str.AppendLiteral("}\n"); + fprintf_stderr(out, "%s", str.get()); +} +#endif + +bool +Declaration::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const +{ + aReturn.Truncate(); + if (aIndex < mOrder.Length()) { + nsCSSPropertyID property = GetPropertyAt(aIndex); + if (property == eCSSPropertyExtra_variable) { + GetCustomPropertyNameAt(aIndex, aReturn); + return true; + } + if (0 <= property) { + AppendASCIItoUTF16(nsCSSProps::GetStringValue(property), aReturn); + return true; + } + } + return false; +} + +void +Declaration::InitializeEmpty() +{ + MOZ_ASSERT(!mData && !mImportantData, "already initialized"); + mData = nsCSSCompressedDataBlock::CreateEmptyBlock(); +} + +size_t +Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mOrder.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0; + n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0; + if (mVariables) { + n += mVariables->SizeOfIncludingThis(aMallocSizeOf); + } + if (mImportantVariables) { + n += mImportantVariables->SizeOfIncludingThis(aMallocSizeOf); + } + return n; +} + +void +Declaration::GetVariableValue(const nsAString& aName, nsAString& aValue) const +{ + aValue.Truncate(); + + CSSVariableDeclarations::Type type; + nsString value; + + if ((mImportantVariables && mImportantVariables->Get(aName, type, value)) || + (mVariables && mVariables->Get(aName, type, value))) { + switch (type) { + case CSSVariableDeclarations::eTokenStream: + aValue.Append(value); + break; + + case CSSVariableDeclarations::eInitial: + aValue.AppendLiteral("initial"); + break; + + case CSSVariableDeclarations::eInherit: + aValue.AppendLiteral("inherit"); + break; + + case CSSVariableDeclarations::eUnset: + aValue.AppendLiteral("unset"); + break; + + default: + MOZ_ASSERT(false, "unexpected variable value type"); + } + } +} + +void +Declaration::AddVariable(const nsAString& aName, + CSSVariableDeclarations::Type aType, + const nsString& aValue, + bool aIsImportant, + bool aOverrideImportant) +{ + MOZ_ASSERT(IsMutable()); + + nsTArray::index_type index = mVariableOrder.IndexOf(aName); + if (index == nsTArray::NoIndex) { + index = mVariableOrder.Length(); + mVariableOrder.AppendElement(aName); + } + + if (!aIsImportant && !aOverrideImportant && + mImportantVariables && mImportantVariables->Has(aName)) { + return; + } + + CSSVariableDeclarations* variables; + if (aIsImportant) { + if (mVariables) { + mVariables->Remove(aName); + } + if (!mImportantVariables) { + mImportantVariables = new CSSVariableDeclarations; + } + variables = mImportantVariables; + } else { + if (mImportantVariables) { + mImportantVariables->Remove(aName); + } + if (!mVariables) { + mVariables = new CSSVariableDeclarations; + } + variables = mVariables; + } + + switch (aType) { + case CSSVariableDeclarations::eTokenStream: + variables->PutTokenStream(aName, aValue); + break; + + case CSSVariableDeclarations::eInitial: + MOZ_ASSERT(aValue.IsEmpty()); + variables->PutInitial(aName); + break; + + case CSSVariableDeclarations::eInherit: + MOZ_ASSERT(aValue.IsEmpty()); + variables->PutInherit(aName); + break; + + case CSSVariableDeclarations::eUnset: + MOZ_ASSERT(aValue.IsEmpty()); + variables->PutUnset(aName); + break; + + default: + MOZ_ASSERT(false, "unexpected aType value"); + } + + uint32_t propertyIndex = index + eCSSProperty_COUNT; + mOrder.RemoveElement(propertyIndex); + mOrder.AppendElement(propertyIndex); +} + +void +Declaration::RemoveVariable(const nsAString& aName) +{ + if (mVariables) { + mVariables->Remove(aName); + } + if (mImportantVariables) { + mImportantVariables->Remove(aName); + } + nsTArray::index_type index = mVariableOrder.IndexOf(aName); + if (index != nsTArray::NoIndex) { + mOrder.RemoveElement(index + eCSSProperty_COUNT); + } +} + +bool +Declaration::GetVariableIsImportant(const nsAString& aName) const +{ + return mImportantVariables && mImportantVariables->Has(aName); +} + +} // namespace css +} // namespace mozilla diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h new file mode 100644 index 000000000..a18c38b5d --- /dev/null +++ b/layout/style/Declaration.h @@ -0,0 +1,417 @@ +/* -*- 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/. */ + +/* + * representation of a declaration block (or style attribute) in a CSS + * stylesheet + */ + +#ifndef mozilla_css_Declaration_h +#define mozilla_css_Declaration_h + +// This header is in EXPORTS because it's used in several places in content/, +// but it's not really a public interface. +#ifndef MOZILLA_INTERNAL_API +#error "This file should only be included within libxul" +#endif + +#include "mozilla/Attributes.h" +#include "mozilla/DeclarationBlock.h" +#include "mozilla/MemoryReporting.h" +#include "CSSVariableDeclarations.h" +#include "nsCSSDataBlock.h" +#include "nsCSSPropertyID.h" +#include "nsCSSProps.h" +#include "nsIStyleRule.h" +#include "nsStringFwd.h" +#include "nsTArray.h" +#include + +// feec07b8-3fe6-491e-90d5-cc93f853e048 +#define NS_CSS_DECLARATION_IMPL_CID \ +{ 0xfeec07b8, 0x3fe6, 0x491e, \ + { 0x90, 0xd5, 0xcc, 0x93, 0xf8, 0x53, 0xe0, 0x48 } } + +class nsHTMLCSSStyleSheet; + +namespace mozilla { +namespace css { + +class Rule; +class Declaration; + +/** + * ImportantStyleData is the implementation of nsIStyleRule (a source of + * style data) representing the style data coming from !important rules; + * the !important declarations need a separate nsIStyleRule object since + * they fit at a different point in the cascade. + * + * ImportantStyleData is allocated only as part of a Declaration object. + */ +class ImportantStyleData final : public nsIStyleRule +{ +public: + + NS_DECL_ISUPPORTS + + inline ::mozilla::css::Declaration* Declaration(); + + // nsIStyleRule interface + virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; + virtual bool MightMapInheritedStyleData() override; + virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + +private: + ImportantStyleData() {} + ~ImportantStyleData() {} + + friend class ::mozilla::css::Declaration; +}; + +// Declaration objects have unusual lifetime rules. Every declaration +// begins life in an invalid state which ends when InitializeEmpty or +// CompressFrom is called upon it. After that, it can be attached to +// exactly one style rule, and will be destroyed when that style rule +// is destroyed. A declaration becomes immutable (via a SetImmutable +// call) when it is matched (put in the rule tree); after that, it must +// be copied before it can be modified, which is taken care of by +// |EnsureMutable|. + +class Declaration final : public DeclarationBlock + , public nsIStyleRule +{ +public: + /** + * Construct an |Declaration| that is in an invalid state (null + * |mData|) and cannot be used until its |CompressFrom| method or + * |InitializeEmpty| method is called. + */ + Declaration() : DeclarationBlock(StyleBackendType::Gecko) {} + + Declaration(const Declaration& aCopy); + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID) + + NS_DECL_ISUPPORTS + +private: + ~Declaration(); + +public: + + // nsIStyleRule implementation + virtual void MapRuleInfoInto(nsRuleData *aRuleData) override; + virtual bool MightMapInheritedStyleData() override; + virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, + nsCSSValue* aValue) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + + /** + * |ValueAppended| must be called to maintain this declaration's + * |mOrder| whenever a property is parsed into an expanded data block + * for this declaration. aProperty must not be a shorthand. + */ + void ValueAppended(nsCSSPropertyID aProperty); + + void GetPropertyValue(const nsAString& aProperty, nsAString& aValue) const; + void GetPropertyValueByID(nsCSSPropertyID aPropID, nsAString& aValue) const; + void GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + bool GetPropertyIsImportant(const nsAString& aProperty) const; + void RemoveProperty(const nsAString& aProperty); + void RemovePropertyByID(nsCSSPropertyID aProperty); + + bool HasProperty(nsCSSPropertyID aProperty) const; + + bool HasImportantData() const { + return mImportantData || mImportantVariables; + } + + /** + * Adds a custom property declaration to this object. + * + * @param aName The variable name (i.e., without the "--" prefix). + * @param aType The type of value the variable has. + * @param aValue The value of the variable, if aType is + * CSSVariableDeclarations::eTokenStream. + * @param aIsImportant Whether the declaration is !important. + * @param aOverrideImportant When aIsImportant is false, whether an + * existing !important declaration will be overridden. + */ + void AddVariable(const nsAString& aName, + CSSVariableDeclarations::Type aType, + const nsString& aValue, + bool aIsImportant, + bool aOverrideImportant); + + /** + * Removes a custom property declaration from this object. + * + * @param aName The variable name (i.e., without the "--" prefix). + */ + void RemoveVariable(const nsAString& aName); + + /** + * Gets the string value for a custom property declaration of a variable + * with a given name. + * + * @param aName The variable name (i.e., without the "--" prefix). + * @param aValue Out parameter into which the variable's value will be + * stored. If the value is 'initial' or 'inherit', that exact string + * will be stored in aValue. + */ + void GetVariableValue(const nsAString& aName, nsAString& aValue) const; + + /** + * Returns whether the custom property declaration for a variable with + * the given name was !important. + */ + bool GetVariableIsImportant(const nsAString& aName) const; + + uint32_t Count() const { + return mOrder.Length(); + } + + // Returns whether we actually had a property at aIndex + bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; + + void ToString(nsAString& aString) const; + + nsCSSCompressedDataBlock* GetNormalBlock() const { return mData; } + nsCSSCompressedDataBlock* GetImportantBlock() const { return mImportantData; } + + void AssertNotExpanded() const { + MOZ_ASSERT(mData, "should only be called when not expanded"); + } + + /** + * Initialize this declaration as holding no data. Cannot fail. + */ + void InitializeEmpty(); + + /** + * Transfer all of the state from |aExpandedData| into this declaration. + * After calling, |aExpandedData| should be in its initial state. + * Callers must make sure mOrder is updated as necessary. + */ + void CompressFrom(nsCSSExpandedDataBlock *aExpandedData) { + MOZ_ASSERT(!mData, "oops"); + MOZ_ASSERT(!mImportantData, "oops"); + aExpandedData->Compress(getter_Transfers(mData), + getter_Transfers(mImportantData), + mOrder); + aExpandedData->AssertInitialState(); + } + + /** + * Transfer all of the state from this declaration into + * |aExpandedData| and put this declaration temporarily into an + * invalid state (ended by |CompressFrom| or |InitializeEmpty|) that + * should last only during parsing. During this time only + * |ValueAppended| should be called. + */ + void ExpandTo(nsCSSExpandedDataBlock *aExpandedData) { + AssertMutable(); + aExpandedData->AssertInitialState(); + + MOZ_ASSERT(mData, "oops"); + aExpandedData->Expand(mData.forget(), mImportantData.forget()); + } + + void MapImportantRuleInfoInto(nsRuleData *aRuleData) const { + AssertNotExpanded(); + MOZ_ASSERT(mImportantData || mImportantVariables, + "must have important data or variables"); + if (mImportantData) { + mImportantData->MapRuleInfoInto(aRuleData); + } + if (mImportantVariables) { + mImportantVariables->MapRuleInfoInto(aRuleData); + } + } + + bool MapsImportantInheritedStyleData() const; + + /** + * Attempt to replace the value for |aProperty| stored in this + * declaration with the matching value from |aFromBlock|. + * This method may only be called on a mutable declaration. + * It will fail (returning false) if |aProperty| is shorthand, + * is not already in this declaration, or does not have the indicated + * importance level. If it returns true, it erases the value in + * |aFromBlock|. |aChanged| is set to true if the declaration + * changed as a result of the call, and to false otherwise. + */ + bool TryReplaceValue(nsCSSPropertyID aProperty, bool aIsImportant, + nsCSSExpandedDataBlock& aFromBlock, + bool* aChanged) + { + AssertMutable(); + AssertNotExpanded(); + + if (nsCSSProps::IsShorthand(aProperty)) { + *aChanged = false; + return false; + } + nsCSSCompressedDataBlock *block = aIsImportant ? mImportantData : mData; + // mImportantData might be null + if (!block) { + *aChanged = false; + return false; + } + +#ifdef DEBUG + { + nsCSSCompressedDataBlock *other = aIsImportant ? mData : mImportantData; + MOZ_ASSERT(!other || !other->ValueFor(aProperty) || + !block->ValueFor(aProperty), + "Property both important and not?"); + } +#endif + return block->TryReplaceValue(aProperty, aFromBlock, aChanged); + } + + bool HasNonImportantValueFor(nsCSSPropertyID aProperty) const { + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), "must be longhand"); + return !!mData->ValueFor(aProperty); + } + + /** + * Clear the data, in preparation for its replacement with entirely + * new data by a call to |CompressFrom|. + */ + void ClearData() { + AssertMutable(); + mData = nullptr; + mImportantData = nullptr; + mVariables = nullptr; + mImportantVariables = nullptr; + mOrder.Clear(); + mVariableOrder.Clear(); + } + + ImportantStyleData* GetImportantStyleData() { + if (HasImportantData()) { + return &mImportantStyleData; + } + return nullptr; + } + +private: + Declaration& operator=(const Declaration& aCopy) = delete; + bool operator==(const Declaration& aCopy) const = delete; + + void GetPropertyValueInternal(nsCSSPropertyID aProperty, nsAString& aValue, + nsCSSValue::Serialization aValueSerialization) + const; + bool GetPropertyIsImportantByID(nsCSSPropertyID aProperty) const; + + static void AppendImportanceToString(bool aIsImportant, nsAString& aString); + // return whether there was a value in |aValue| (i.e., it had a non-null unit) + bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult) const; + bool AppendValueToString(nsCSSPropertyID aProperty, nsAString& aResult, + nsCSSValue::Serialization aValueSerialization) const; + // Helper for ToString with strange semantics regarding aValue. + void AppendPropertyAndValueToString(nsCSSPropertyID aProperty, + nsAutoString& aValue, + nsAString& aResult) const; + // helper for ToString that serializes a custom property declaration for + // a variable with the specified name + void AppendVariableAndValueToString(const nsAString& aName, + nsAString& aResult) const; + + void GetImageLayerValue(nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSPropertyID aTable[]) const; + + void GetImageLayerPositionValue(nsCSSCompressedDataBlock *data, + nsAString& aValue, + nsCSSValue::Serialization aSerialization, + const nsCSSPropertyID aTable[]) const; + +public: + /** + * Returns the property at the given index in the ordered list of + * declarations. For custom properties, eCSSPropertyExtra_variable + * is returned. + */ + nsCSSPropertyID GetPropertyAt(uint32_t aIndex) const { + uint32_t value = mOrder[aIndex]; + if (value >= eCSSProperty_COUNT) { + return eCSSPropertyExtra_variable; + } + return nsCSSPropertyID(value); + } + + /** + * Gets the name of the custom property at the given index in the ordered + * list of declarations. + */ + void GetCustomPropertyNameAt(uint32_t aIndex, nsAString& aResult) const { + MOZ_ASSERT(mOrder[aIndex] >= eCSSProperty_COUNT); + uint32_t variableIndex = mOrder[aIndex] - eCSSProperty_COUNT; + aResult.Truncate(); + aResult.AppendLiteral("--"); + aResult.Append(mVariableOrder[variableIndex]); + } + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + // The order of properties in this declaration. Longhand properties are + // represented by their nsCSSPropertyID value, and each custom property (--*) + // is represented by a value that begins at eCSSProperty_COUNT. + // + // Subtracting eCSSProperty_COUNT from those values that represent custom + // properties results in an index into mVariableOrder, which identifies the + // specific variable the custom property declaration is for. + AutoTArray mOrder; + + // variable names of custom properties found in mOrder + nsTArray mVariableOrder; + + // never null, except while expanded, or before the first call to + // InitializeEmpty or CompressFrom. + nsAutoPtr mData; + + // may be null + nsAutoPtr mImportantData; + + // may be null + nsAutoPtr mVariables; + + // may be null + nsAutoPtr mImportantVariables; + + friend class ImportantStyleData; + ImportantStyleData mImportantStyleData; +}; + +inline ::mozilla::css::Declaration* +ImportantStyleData::Declaration() +{ + union { + char* ch; /* for pointer arithmetic */ + ::mozilla::css::Declaration* declaration; + ImportantStyleData* importantData; + } u; + u.importantData = this; + u.ch -= offsetof(::mozilla::css::Declaration, mImportantStyleData); + return u.declaration; +} + +NS_DEFINE_STATIC_IID_ACCESSOR(Declaration, NS_CSS_DECLARATION_IMPL_CID) + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_Declaration_h */ diff --git a/layout/style/DeclarationBlock.h b/layout/style/DeclarationBlock.h new file mode 100644 index 000000000..c3ed663b4 --- /dev/null +++ b/layout/style/DeclarationBlock.h @@ -0,0 +1,146 @@ +/* -*- 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/ServoUtils.h" +#include "mozilla/StyleBackendType.h" + +#include "nsCSSPropertyID.h" + +class nsHTMLCSSStyleSheet; + +namespace mozilla { + +class ServoDeclarationBlock; + +namespace css { +class Declaration; +class Rule; +} // namespace css + +class DeclarationBlock +{ +protected: + explicit DeclarationBlock(StyleBackendType aType) + : mImmutable(false), mType(aType) { mContainer.mRaw = 0; } + + DeclarationBlock(const DeclarationBlock& aCopy) + : DeclarationBlock(aCopy.mType) {} + +public: + MOZ_DECL_STYLO_METHODS(css::Declaration, ServoDeclarationBlock) + + inline MozExternalRefCountType AddRef(); + inline MozExternalRefCountType Release(); + + inline already_AddRefed Clone() const; + + /** + * Return whether |this| may be modified. + */ + bool IsMutable() const { + return !mImmutable; + } + + /** + * Crash if |this| cannot be modified. + */ + void AssertMutable() const { + MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable"); + } + + /** + * Mark this declaration as unmodifiable. It's 'const' so it can + * be called from ToString. + */ + void SetImmutable() const { mImmutable = true; } + + /** + * Copy |this|, if necessary to ensure that it can be modified. + */ + inline already_AddRefed EnsureMutable(); + + 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 SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) { + MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet, + "should never overwrite one sheet with another"); + mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet; + if (aHTMLCSSStyleSheet) { + mContainer.mRaw |= uintptr_t(1); + } + } + + nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const { + if (!(mContainer.mRaw & 0x1)) { + return nullptr; + } + auto c = mContainer; + c.mRaw &= ~uintptr_t(1); + return c.mHTMLCSSStyleSheet; + } + + inline void ToString(nsAString& aString) const; + + inline uint32_t Count() const; + inline bool GetNthProperty(uint32_t aIndex, nsAString& aReturn) const; + + inline void GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + inline void GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const; + inline void GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const; + inline bool GetPropertyIsImportant(const nsAString& aProperty) const; + inline void RemoveProperty(const nsAString& aProperty); + inline void RemovePropertyByID(nsCSSPropertyID aProperty); + +private: + union { + // We only ever have one of these since we have an + // nsHTMLCSSStyleSheet only for style attributes, and style + // attributes never have an owning rule. + + // It's an nsHTMLCSSStyleSheet if the low bit is set. + + uintptr_t mRaw; + + // The style rule that owns this declaration. May be null. + css::Rule* mOwningRule; + + // The nsHTMLCSSStyleSheet that is responsible for this declaration. + // Only non-null for style attributes. + nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet; + } mContainer; + + // set when declaration put in the rule tree; + // also by ToString (hence the 'mutable'). + mutable bool mImmutable; + + const StyleBackendType mType; +}; + +} // namespace mozilla + +#endif // mozilla_DeclarationBlock_h diff --git a/layout/style/DeclarationBlockInlines.h b/layout/style/DeclarationBlockInlines.h new file mode 100644 index 000000000..791d24498 --- /dev/null +++ b/layout/style/DeclarationBlockInlines.h @@ -0,0 +1,114 @@ +/* -*- 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_DeclarationBlockInlines_h +#define mozilla_DeclarationBlockInlines_h + +#include "mozilla/css/Declaration.h" +#include "mozilla/ServoDeclarationBlock.h" + +namespace mozilla { + +MOZ_DEFINE_STYLO_METHODS(DeclarationBlock, css::Declaration, ServoDeclarationBlock) + +MozExternalRefCountType +DeclarationBlock::AddRef() +{ + MOZ_STYLO_FORWARD(AddRef, ()) +} + +MozExternalRefCountType +DeclarationBlock::Release() +{ + MOZ_STYLO_FORWARD(Release, ()) +} + +already_AddRefed +DeclarationBlock::Clone() const +{ + RefPtr result; + if (IsGecko()) { + result = new css::Declaration(*AsGecko()); + } else { + result = new ServoDeclarationBlock(*AsServo()); + } + return result.forget(); +} + +already_AddRefed +DeclarationBlock::EnsureMutable() +{ +#ifdef DEBUG + if (IsGecko()) { + AsGecko()->AssertNotExpanded(); + } +#endif + if (!IsMutable()) { + return Clone(); + } + return do_AddRef(this); +} + +void +DeclarationBlock::ToString(nsAString& aString) const +{ + MOZ_STYLO_FORWARD(ToString, (aString)) +} + +uint32_t +DeclarationBlock::Count() const +{ + MOZ_STYLO_FORWARD(Count, ()) +} + +bool +DeclarationBlock::GetNthProperty(uint32_t aIndex, nsAString& aReturn) const +{ + MOZ_STYLO_FORWARD(GetNthProperty, (aIndex, aReturn)) +} + +void +DeclarationBlock::GetPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetPropertyValue, (aProperty, aValue)) +} + +void +DeclarationBlock::GetPropertyValueByID(nsCSSPropertyID aPropID, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetPropertyValueByID, (aPropID, aValue)) +} + +void +DeclarationBlock::GetAuthoredPropertyValue(const nsAString& aProperty, + nsAString& aValue) const +{ + MOZ_STYLO_FORWARD(GetAuthoredPropertyValue, (aProperty, aValue)) +} + +bool +DeclarationBlock::GetPropertyIsImportant(const nsAString& aProperty) const +{ + MOZ_STYLO_FORWARD(GetPropertyIsImportant, (aProperty)) +} + +void +DeclarationBlock::RemoveProperty(const nsAString& aProperty) +{ + MOZ_STYLO_FORWARD(RemoveProperty, (aProperty)) +} + +void +DeclarationBlock::RemovePropertyByID(nsCSSPropertyID aProperty) +{ + MOZ_STYLO_FORWARD(RemovePropertyByID, (aProperty)) +} + +} // namespace mozilla + +#endif // mozilla_DeclarationBlockInlines_h diff --git a/layout/style/ErrorReporter.cpp b/layout/style/ErrorReporter.cpp new file mode 100644 index 000000000..56a84da3e --- /dev/null +++ b/layout/style/ErrorReporter.cpp @@ -0,0 +1,382 @@ +/* -*- 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/. */ + +/* diagnostic reporting for CSS style sheet parser */ + +#include "mozilla/css/ErrorReporter.h" + +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Loader.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "nsCSSScanner.h" +#include "nsIConsoleService.h" +#include "nsIDocument.h" +#include "nsIFactory.h" +#include "nsIScriptError.h" +#include "nsIStringBundle.h" +#include "nsServiceManagerUtils.h" +#include "nsStyleUtil.h" +#include "nsThreadUtils.h" + +#ifdef CSS_REPORT_PARSE_ERRORS + +using namespace mozilla; + +namespace { +class ShortTermURISpecCache : public Runnable { +public: + ShortTermURISpecCache() : mPending(false) {} + + nsString const& GetSpec(nsIURI* aURI) { + if (mURI != aURI) { + mURI = aURI; + + nsAutoCString cSpec; + nsresult rv = mURI->GetSpec(cSpec); + if (NS_FAILED(rv)) { + cSpec.AssignLiteral("[nsIURI::GetSpec failed]"); + } + CopyUTF8toUTF16(cSpec, mSpec); + } + 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 + +static bool sReportErrors; +static nsIConsoleService *sConsoleService; +static nsIFactory *sScriptErrorFactory; +static nsIStringBundle *sStringBundle; +static ShortTermURISpecCache *sSpecCache; + +#define CSS_ERRORS_PREF "layout.css.report_errors" + +static bool +InitGlobals() +{ + MOZ_ASSERT(!sConsoleService && !sScriptErrorFactory && !sStringBundle, + "should not have been called"); + + if (NS_FAILED(Preferences::AddBoolVarCache(&sReportErrors, CSS_ERRORS_PREF, + true))) { + return false; + } + + nsCOMPtr cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (!cs) { + return false; + } + + nsCOMPtr sf = do_GetClassObject(NS_SCRIPTERROR_CONTRACTID); + if (!sf) { + return false; + } + + nsCOMPtr sbs = services::GetStringBundleService(); + if (!sbs) { + return false; + } + + nsCOMPtr sb; + nsresult rv = sbs->CreateBundle("chrome://global/locale/css.properties", + getter_AddRefs(sb)); + if (NS_FAILED(rv) || !sb) { + return false; + } + + cs.forget(&sConsoleService); + sf.forget(&sScriptErrorFactory); + sb.forget(&sStringBundle); + + return true; +} + +static inline bool +ShouldReportErrors() +{ + if (!sConsoleService) { + if (!InitGlobals()) { + return false; + } + } + return sReportErrors; +} + +namespace mozilla { +namespace css { + +/* static */ void +ErrorReporter::ReleaseGlobals() +{ + NS_IF_RELEASE(sConsoleService); + NS_IF_RELEASE(sScriptErrorFactory); + NS_IF_RELEASE(sStringBundle); + NS_IF_RELEASE(sSpecCache); +} + +ErrorReporter::ErrorReporter(const nsCSSScanner& aScanner, + const CSSStyleSheet* aSheet, + const Loader* aLoader, + nsIURI* aURI) + : mScanner(&aScanner), mSheet(aSheet), mLoader(aLoader), mURI(aURI), + mInnerWindowID(0), mErrorLineNumber(0), mPrevErrorLineNumber(0), + mErrorColNumber(0) +{ +} + +ErrorReporter::~ErrorReporter() +{ + // 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()) { + if (NS_FAILED(NS_DispatchToCurrentThread(sSpecCache))) { + // Peform the "deferred" cleanup immediately if the dispatch fails. + sSpecCache->Run(); + } else { + sSpecCache->SetPending(); + } + } +} + +void +ErrorReporter::OutputError() +{ + if (mError.IsEmpty()) { + return; + } + if (!ShouldReportErrors()) { + ClearError(); + return; + } + + if (mInnerWindowID == 0 && (mSheet || mLoader)) { + if (mSheet) { + mInnerWindowID = mSheet->FindOwningWindowInnerID(); + } + if (mInnerWindowID == 0 && mLoader) { + nsIDocument* doc = mLoader->GetDocument(); + if (doc) { + mInnerWindowID = doc->InnerWindowID(); + } + } + // don't attempt this again, even if we failed + mSheet = nullptr; + mLoader = nullptr; + } + + if (mFileName.IsEmpty()) { + if (mURI) { + if (!sSpecCache) { + sSpecCache = new ShortTermURISpecCache; + NS_ADDREF(sSpecCache); + } + mFileName = sSpecCache->GetSpec(mURI); + mURI = nullptr; + } else { + mFileName.AssignLiteral("from DOM"); + } + } + + nsresult rv; + nsCOMPtr errorObject = + do_CreateInstance(sScriptErrorFactory, &rv); + + if (NS_SUCCEEDED(rv)) { + rv = errorObject->InitWithWindowID(mError, + mFileName, + mErrorLine, + mErrorLineNumber, + mErrorColNumber, + nsIScriptError::warningFlag, + "CSS Parser", + mInnerWindowID); + if (NS_SUCCEEDED(rv)) { + sConsoleService->LogMessage(errorObject); + } + } + + ClearError(); +} + +void +ErrorReporter::OutputError(uint32_t aLineNumber, uint32_t aLineOffset) +{ + mErrorLineNumber = aLineNumber; + mErrorColNumber = aLineOffset; + OutputError(); +} + +void +ErrorReporter::ClearError() +{ + mError.Truncate(); +} + +void +ErrorReporter::AddToError(const nsString &aErrorText) +{ + if (!ShouldReportErrors()) return; + + if (mError.IsEmpty()) { + mError = aErrorText; + mErrorLineNumber = mScanner->GetLineNumber(); + mErrorColNumber = mScanner->GetColumnNumber(); + // Retrieve the error line once per line, and reuse the same nsString + // for all errors on that line. That causes the text of the line to + // be shared among all the nsIScriptError objects. + if (mErrorLine.IsEmpty() || mErrorLineNumber != mPrevErrorLineNumber) { + // Be careful here: the error line might be really long and OOM + // when we try to make a copy here. If so, just leave it empty. + if (!mErrorLine.Assign(mScanner->GetCurrentLine(), fallible)) { + mErrorLine.Truncate(); + } + mPrevErrorLineNumber = mErrorLineNumber; + } + } else { + mError.AppendLiteral(" "); + mError.Append(aErrorText); + } +} + +void +ErrorReporter::ReportUnexpected(const char *aMessage) +{ + if (!ShouldReportErrors()) return; + + nsAutoString str; + sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpected(const char *aMessage, + const nsString &aParam) +{ + if (!ShouldReportErrors()) return; + + nsAutoString qparam; + nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam); + const char16_t *params[1] = { qparam.get() }; + + nsAutoString str; + sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpected(const char *aMessage, + const nsCSSToken &aToken) +{ + if (!ShouldReportErrors()) return; + + nsAutoString tokenString; + aToken.AppendToString(tokenString); + const char16_t *params[1] = { tokenString.get() }; + + nsAutoString str; + sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpected(const char *aMessage, + const nsCSSToken &aToken, + char16_t aChar) +{ + if (!ShouldReportErrors()) return; + + nsAutoString tokenString; + aToken.AppendToString(tokenString); + const char16_t charStr[2] = { aChar, 0 }; + const char16_t *params[2] = { tokenString.get(), charStr }; + + nsAutoString str; + sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpected(const char *aMessage, + const nsString &aParam, + const nsString &aValue) +{ + if (!ShouldReportErrors()) return; + + nsAutoString qparam; + nsStyleUtil::AppendEscapedCSSIdent(aParam, qparam); + const char16_t *params[2] = { qparam.get(), aValue.get() }; + + nsAutoString str; + sStringBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpectedEOF(const char *aMessage) +{ + if (!ShouldReportErrors()) return; + + nsAutoString innerStr; + sStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aMessage).get(), + getter_Copies(innerStr)); + const char16_t *params[1] = { innerStr.get() }; + + nsAutoString str; + sStringBundle->FormatStringFromName(u"PEUnexpEOF2", + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +void +ErrorReporter::ReportUnexpectedEOF(char16_t aExpected) +{ + if (!ShouldReportErrors()) return; + + const char16_t expectedStr[] = { + char16_t('\''), aExpected, char16_t('\''), char16_t(0) + }; + const char16_t *params[1] = { expectedStr }; + + nsAutoString str; + sStringBundle->FormatStringFromName(u"PEUnexpEOF2", + params, ArrayLength(params), + getter_Copies(str)); + AddToError(str); +} + +} // namespace css +} // namespace mozilla + +#endif diff --git a/layout/style/ErrorReporter.h b/layout/style/ErrorReporter.h new file mode 100644 index 000000000..f3d7fa1da --- /dev/null +++ b/layout/style/ErrorReporter.h @@ -0,0 +1,113 @@ +/* -*- 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/. */ + +/* diagnostic reporting for CSS style sheet parser */ + +#ifndef mozilla_css_ErrorReporter_h_ +#define mozilla_css_ErrorReporter_h_ + +// XXX turn this off for minimo builds +#define CSS_REPORT_PARSE_ERRORS + +#include "nsString.h" + +struct nsCSSToken; +class nsCSSScanner; +class nsIURI; + +namespace mozilla { +class CSSStyleSheet; + +namespace css { + +class Loader; + +// If CSS_REPORT_PARSE_ERRORS is not defined, all of this class's +// methods become inline stubs. +class MOZ_STACK_CLASS ErrorReporter { +public: + ErrorReporter(const nsCSSScanner &aScanner, + const CSSStyleSheet *aSheet, + const Loader *aLoader, + nsIURI *aURI); + ~ErrorReporter(); + + static void ReleaseGlobals(); + + void OutputError(); + void OutputError(uint32_t aLineNumber, uint32_t aLineOffset); + 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, a string + void ReportUnexpected(const char *aMessage, const nsString& aParam); + // one parameter, a token + void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken); + // two parameters, a token and a character, in that order + void ReportUnexpected(const char *aMessage, const nsCSSToken& aToken, + char16_t aChar); + // two parameters, a param and a value + void ReportUnexpected(const char *aMessage, const nsString& aParam, + const nsString& aValue); + + // for ReportUnexpectedEOF, aExpected can be either a stringbundle + // name or a single character. In the former case there may not be + // any format parameters. + void ReportUnexpectedEOF(const char *aExpected); + void ReportUnexpectedEOF(char16_t aExpected); + +private: + void AddToError(const nsString &aErrorText); + +#ifdef CSS_REPORT_PARSE_ERRORS + nsAutoString mError; + nsString mErrorLine; + nsString mFileName; + const nsCSSScanner *mScanner; + const CSSStyleSheet *mSheet; + const Loader *mLoader; + nsIURI *mURI; + uint64_t mInnerWindowID; + uint32_t mErrorLineNumber; + uint32_t mPrevErrorLineNumber; + uint32_t mErrorColNumber; +#endif +}; + +#ifndef CSS_REPORT_PARSE_ERRORS +inline ErrorReporter::ErrorReporter(const nsCSSScanner&, + const CSSStyleSheet*, + const Loader*, + nsIURI*) {} +inline ErrorReporter::~ErrorReporter() {} + +inline void ErrorReporter::ReleaseGlobals() {} + +inline void ErrorReporter::OutputError() {} +inline void ErrorReporter::ClearError() {} + +inline void ErrorReporter::ReportUnexpected(const char *) {} +inline void ErrorReporter::ReportUnexpected(const char *, const nsString &) {} +inline void ErrorReporter::ReportUnexpected(const char *, const nsCSSToken &) {} +inline void ErrorReporter::ReportUnexpected(const char *, const nsCSSToken &, + char16_t) {} +inline void ErrorReporter::ReportUnexpected(const char *, const nsString &, + const nsString &) {} + +inline void ErrorReporter::ReportUnexpectedEOF(const char *) {} +inline void ErrorReporter::ReportUnexpectedEOF(char16_t) {} + +inline void ErrorReporter::AddToError(const nsString &) {} +#endif + +} // 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 000000000..4558ab1a2 --- /dev/null +++ b/layout/style/FontFace.cpp @@ -0,0 +1,808 @@ +/* -*- 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/. */ + +#include "mozilla/dom/FontFace.h" + +#include +#include "mozilla/dom/FontFaceBinding.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 "nsCSSParser.h" +#include "nsCSSRules.h" +#include "nsIDocument.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: + explicit FontFaceBufferSource(FontFace* aFontFace) + : mFontFace(aFontFace) {} + virtual void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength); + +private: + RefPtr mFontFace; +}; + +void +FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) +{ + MOZ_ASSERT(mFontFace, "only call TakeBuffer once on a given " + "FontFaceBufferSource object"); + mFontFace->TakeBuffer(aBuffer, aLength); + mFontFace = nullptr; +} + +// -- Utility functions ------------------------------------------------------ + +template +static void +GetDataFrom(const T& aObject, uint8_t*& aBuffer, uint32_t& aLength) +{ + MOZ_ASSERT(!aBuffer); + aObject.ComputeLengthAndData(); + // We use malloc here rather than a FallibleTArray or fallible + // operator new[] since the gfxUserFontEntry will be calling free + // on it. + aBuffer = (uint8_t*) malloc(aObject.Length()); + if (!aBuffer) { + return; + } + memcpy((void*) aBuffer, aObject.Data(), aObject.Length()); + aLength = aObject.Length(); +} + +// -- 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(mRule) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +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) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets) + 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(nsISupports* aParent, FontFaceSet* aFontFaceSet) + : mParent(aParent) + , mLoadedRejection(NS_OK) + , mStatus(FontFaceLoadStatus::Unloaded) + , mSourceType(SourceType(0)) + , mSourceBuffer(nullptr) + , mSourceBufferLength(0) + , mFontFaceSet(aFontFaceSet) + , mInFontFaceSet(false) +{ + MOZ_COUNT_CTOR(FontFace); +} + +FontFace::~FontFace() +{ + MOZ_COUNT_DTOR(FontFace); + + SetUserFontEntry(nullptr); + + if (mSourceBuffer) { + free(mSourceBuffer); + } +} + +JSObject* +FontFace::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return FontFaceBinding::Wrap(aCx, this, aGivenProto); +} + +static FontFaceLoadStatus +LoadStateToStatus(gfxUserFontEntry::UserFontLoadState aLoadState) +{ + switch (aLoadState) { + case gfxUserFontEntry::UserFontLoadState::STATUS_NOT_LOADED: + return FontFaceLoadStatus::Unloaded; + case gfxUserFontEntry::UserFontLoadState::STATUS_LOADING: + return FontFaceLoadStatus::Loading; + case gfxUserFontEntry::UserFontLoadState::STATUS_LOADED: + return FontFaceLoadStatus::Loaded; + case gfxUserFontEntry::UserFontLoadState::STATUS_FAILED: + return FontFaceLoadStatus::Error; + } + NS_NOTREACHED("invalid aLoadState value"); + return FontFaceLoadStatus::Error; +} + +already_AddRefed +FontFace::CreateForRule(nsISupports* aGlobal, + FontFaceSet* aFontFaceSet, + nsCSSFontFaceRule* aRule) +{ + RefPtr obj = new FontFace(aGlobal, aFontFaceSet); + obj->mRule = aRule; + obj->mSourceType = eSourceType_FontFaceRule; + obj->mInFontFaceSet = true; + return obj.forget(); +} + +already_AddRefed +FontFace::Constructor(const GlobalObject& aGlobal, + const nsAString& aFamily, + const StringOrArrayBufferOrArrayBufferView& aSource, + const FontFaceDescriptors& aDescriptors, + ErrorResult& aRv) +{ + nsISupports* global = aGlobal.GetAsSupports(); + nsCOMPtr window = do_QueryInterface(global); + nsIDocument* doc = window->GetDoc(); + if (!doc) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr obj = new FontFace(global, doc->Fonts()); + if (!obj->SetDescriptors(aFamily, aDescriptors)) { + return obj.forget(); + } + + obj->InitializeSource(aSource); + return obj.forget(); +} + +void +FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource) +{ + if (aSource.IsString()) { + if (!ParseDescriptor(eCSSFontDesc_Src, + aSource.GetAsString(), + mDescriptors->mSrc)) { + Reject(NS_ERROR_DOM_SYNTAX_ERR); + + SetStatus(FontFaceLoadStatus::Error); + return; + } + + mSourceType = eSourceType_URLs; + return; + } + + mSourceType = FontFace::eSourceType_Buffer; + + if (aSource.IsArrayBuffer()) { + GetDataFrom(aSource.GetAsArrayBuffer(), + mSourceBuffer, mSourceBufferLength); + } else { + MOZ_ASSERT(aSource.IsArrayBufferView()); + GetDataFrom(aSource.GetAsArrayBufferView(), + mSourceBuffer, mSourceBufferLength); + } + + SetStatus(FontFaceLoadStatus::Loading); + DoLoad(); +} + +void +FontFace::GetFamily(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + + // Serialize the same way as in nsCSSFontFaceStyleDecl::GetPropertyValue. + nsCSSValue value; + GetDesc(eCSSFontDesc_Family, value); + + aResult.Truncate(); + + if (value.GetUnit() == eCSSUnit_Null) { + return; + } + + nsDependentString family(value.GetStringBufferValue()); + if (!family.IsEmpty()) { + // The string length can be zero when the author passed an invalid + // family name or an invalid descriptor to the JS FontFace constructor. + nsStyleUtil::AppendEscapedCSSString(family, aResult); + } +} + +void +FontFace::SetFamily(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_Family, aValue, aRv); +} + +void +FontFace::GetStyle(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult); +} + +void +FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_Style, aValue, aRv); +} + +void +FontFace::GetWeight(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult); +} + +void +FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_Weight, aValue, aRv); +} + +void +FontFace::GetStretch(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult); +} + +void +FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv); +} + +void +FontFace::GetUnicodeRange(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + + // There is no eCSSProperty_unicode_range for us to pass in to GetDesc + // to get a serialized (possibly defaulted) value, but that function + // doesn't use the property ID for this descriptor anyway. + GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult); +} + +void +FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv); +} + +void +FontFace::GetVariant(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + + // XXX Just expose the font-variant descriptor as "normal" until we + // support it properly (bug 1055385). + aResult.AssignLiteral("normal"); +} + +void +FontFace::SetVariant(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + + // XXX Ignore assignments to variant until we support font-variant + // descriptors (bug 1055385). +} + +void +FontFace::GetFeatureSettings(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings, + aResult); +} + +void +FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv); +} + +void +FontFace::GetDisplay(nsString& aResult) +{ + mFontFaceSet->FlushUserFontSet(); + GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult); +} + +void +FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + SetDescriptor(eCSSFontDesc_Display, aValue, aRv); +} + +FontFaceLoadStatus +FontFace::Status() +{ + return mStatus; +} + +Promise* +FontFace::Load(ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // 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 mLoaded; + } + + // 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(); + + return mLoaded; +} + +gfxUserFontEntry* +FontFace::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"); + + RefPtr newEntry = + mFontFaceSet->FindOrCreateUserFontEntryFromFontFace(this); + if (newEntry) { + SetUserFontEntry(newEntry); + } + } + + return mUserFontEntry; +} + +void +FontFace::DoLoad() +{ + if (!CreateUserFontEntry()) { + return; + } + mUserFontEntry->Load(); +} + +Promise* +FontFace::GetLoaded(ErrorResult& aRv) +{ + mFontFaceSet->FlushUserFontSet(); + + EnsurePromise(); + + if (!mLoaded) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return mLoaded; +} + +void +FontFace::SetStatus(FontFaceLoadStatus aStatus) +{ + 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 (FontFaceSet* otherSet : mOtherFontFaceSets) { + otherSet->OnFontFaceStatusChanged(this); + } + + if (mStatus == FontFaceLoadStatus::Loaded) { + if (mLoaded) { + mLoaded->MaybeResolve(this); + } + } else if (mStatus == FontFaceLoadStatus::Error) { + if (mSourceType == eSourceType_Buffer) { + Reject(NS_ERROR_DOM_SYNTAX_ERR); + } else { + Reject(NS_ERROR_DOM_NETWORK_ERR); + } + } +} + +bool +FontFace::ParseDescriptor(nsCSSFontDesc aDescID, + const nsAString& aString, + nsCSSValue& aResult) +{ + nsCSSParser parser; + + nsCOMPtr global = do_QueryInterface(mParent); + nsCOMPtr principal = global->PrincipalOrNull(); + + nsCOMPtr window = do_QueryInterface(mParent); + nsCOMPtr docURI = window->GetDocumentURI(); + nsCOMPtr base = window->GetDocBaseURI(); + + if (!parser.ParseFontFaceDescriptor(aDescID, aString, + docURI, // aSheetURL + base, + principal, + aResult)) { + aResult.Reset(); + return false; + } + + return true; +} + +void +FontFace::SetDescriptor(nsCSSFontDesc aFontDesc, + const nsAString& aValue, + ErrorResult& aRv) +{ + NS_ASSERTION(!HasRule(), + "we don't handle rule backed FontFace objects yet"); + if (HasRule()) { + return; + } + + nsCSSValue parsedValue; + if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + + mDescriptors->Get(aFontDesc) = parsedValue; + + // XXX Setting descriptors doesn't actually have any effect on FontFace + // objects that have started loading or have already been loaded. +} + +bool +FontFace::SetDescriptors(const nsAString& aFamily, + const FontFaceDescriptors& aDescriptors) +{ + MOZ_ASSERT(!HasRule()); + MOZ_ASSERT(!mDescriptors); + + mDescriptors = new CSSFontFaceDescriptors; + + // Parse all of the mDescriptors in aInitializer, which are the values + // we got from the JS constructor. + if (!ParseDescriptor(eCSSFontDesc_Family, + aFamily, + mDescriptors->mFamily) || + *mDescriptors->mFamily.GetStringBufferValue() == 0 || + !ParseDescriptor(eCSSFontDesc_Style, + aDescriptors.mStyle, + mDescriptors->mStyle) || + !ParseDescriptor(eCSSFontDesc_Weight, + aDescriptors.mWeight, + mDescriptors->mWeight) || + !ParseDescriptor(eCSSFontDesc_Stretch, + aDescriptors.mStretch, + mDescriptors->mStretch) || + !ParseDescriptor(eCSSFontDesc_UnicodeRange, + aDescriptors.mUnicodeRange, + mDescriptors->mUnicodeRange) || + !ParseDescriptor(eCSSFontDesc_FontFeatureSettings, + aDescriptors.mFeatureSettings, + mDescriptors->mFontFeatureSettings) || + !ParseDescriptor(eCSSFontDesc_Display, + aDescriptors.mDisplay, + mDescriptors->mDisplay)) { + // 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 = new CSSFontFaceDescriptors; + + Reject(NS_ERROR_DOM_SYNTAX_ERR); + + SetStatus(FontFaceLoadStatus::Error); + return false; + } + + return true; +} + +void +FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const +{ + if (HasRule()) { + MOZ_ASSERT(mRule); + MOZ_ASSERT(!mDescriptors); + mRule->GetDesc(aDescID, aResult); + } else { + aResult = mDescriptors->Get(aDescID); + } +} + +void +FontFace::GetDesc(nsCSSFontDesc aDescID, + nsCSSPropertyID aPropID, + nsString& aResult) const +{ + MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange || + aDescID == eCSSFontDesc_Display || + aPropID != eCSSProperty_UNKNOWN, + "only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange"); + + nsCSSValue value; + GetDesc(aDescID, value); + + aResult.Truncate(); + + // Fill in a default value for missing descriptors. + if (value.GetUnit() == eCSSUnit_Null) { + 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"); + } + return; + } + + if (aDescID == eCSSFontDesc_UnicodeRange) { + // Since there's no unicode-range property, we can't use + // nsCSSValue::AppendToString to serialize this descriptor. + nsStyleUtil::AppendUnicodeRange(value, aResult); + } else if (aDescID == eCSSFontDesc_Display) { + AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(value.GetIntValue(), + nsCSSProps::kFontDisplayKTable), + aResult); + } else { + value.AppendToString(aPropID, aResult, nsCSSValue::eNormalized); + } +} + +void +FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry) +{ + if (mUserFontEntry) { + mUserFontEntry->mFontFaces.RemoveElement(this); + } + + mUserFontEntry = static_cast(aEntry); + if (mUserFontEntry) { + mUserFontEntry->mFontFaces.AppendElement(this); + + MOZ_ASSERT(mUserFontEntry->GetUserFontSet() == + mFontFaceSet->GetUserFontSet(), + "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 +FontFace::GetFamilyName(nsString& aResult) +{ + nsCSSValue value; + GetDesc(eCSSFontDesc_Family, value); + + if (value.GetUnit() == eCSSUnit_String) { + nsString familyname; + value.GetStringValue(familyname); + aResult.Append(familyname); + } + + return !aResult.IsEmpty(); +} + +void +FontFace::DisconnectFromRule() +{ + MOZ_ASSERT(HasRule()); + + // Make a copy of the descriptors. + mDescriptors = new CSSFontFaceDescriptors; + mRule->GetDescriptors(*mDescriptors); + mRule = nullptr; + mInFontFaceSet = false; +} + +bool +FontFace::HasFontData() const +{ + return mSourceType == eSourceType_Buffer && mSourceBuffer; +} + +void +FontFace::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) +{ + MOZ_ASSERT(HasFontData()); + + aBuffer = mSourceBuffer; + aLength = mSourceBufferLength; + + mSourceBuffer = nullptr; + mSourceBufferLength = 0; +} + +already_AddRefed +FontFace::CreateBufferSource() +{ + RefPtr bufferSource = new FontFaceBufferSource(this); + return bufferSource.forget(); +} + +bool +FontFace::IsInFontFaceSet(FontFaceSet* aFontFaceSet) const +{ + if (mFontFaceSet == aFontFaceSet) { + return mInFontFaceSet; + } + return mOtherFontFaceSets.Contains(aFontFaceSet); +} + +void +FontFace::AddFontFaceSet(FontFaceSet* aFontFaceSet) +{ + MOZ_ASSERT(!IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = true; + } else { + mOtherFontFaceSets.AppendElement(aFontFaceSet); + } +} + +void +FontFace::RemoveFontFaceSet(FontFaceSet* aFontFaceSet) +{ + MOZ_ASSERT(IsInFontFaceSet(aFontFaceSet)); + + if (mFontFaceSet == aFontFaceSet) { + mInFontFaceSet = false; + } else { + mOtherFontFaceSets.RemoveElement(aFontFaceSet); + } +} + +void +FontFace::Reject(nsresult aResult) +{ + if (mLoaded) { + mLoaded->MaybeReject(aResult); + } else if (mLoadedRejection == NS_OK) { + mLoadedRejection = aResult; + } +} + +void +FontFace::EnsurePromise() +{ + if (mLoaded) { + return; + } + + nsCOMPtr global = do_QueryInterface(mParent); + + // If the pref is not set, don't create the Promise (which the page wouldn't + // be able to get to anyway) as it causes the window.FontFace constructor + // to be created. + if (global && FontFaceSet::PrefEnabled()) { + ErrorResult rv; + mLoaded = Promise::Create(global, rv); + + if (mStatus == FontFaceLoadStatus::Loaded) { + mLoaded->MaybeResolve(this); + } else if (mLoadedRejection != NS_OK) { + mLoaded->MaybeReject(mLoadedRejection); + } + } +} + +// -- FontFace::Entry -------------------------------------------------------- + +/* virtual */ void +FontFace::Entry::SetLoadState(UserFontLoadState aLoadState) +{ + gfxUserFontEntry::SetLoadState(aLoadState); + + for (size_t i = 0; i < mFontFaces.Length(); i++) { + mFontFaces[i]->SetStatus(LoadStateToStatus(aLoadState)); + } +} + +/* virtual */ void +FontFace::Entry::GetUserFontSets(nsTArray& aResult) +{ + aResult.Clear(); + + for (FontFace* f : mFontFaces) { + if (f->mInFontFaceSet) { + aResult.AppendElement(f->mFontFaceSet->GetUserFontSet()); + } + for (FontFaceSet* s : f->mOtherFontFaceSets) { + aResult.AppendElement(s->GetUserFontSet()); + } + } + + // Remove duplicates. + aResult.Sort(); + auto it = std::unique(aResult.begin(), aResult.end()); + aResult.TruncateLength(it - aResult.begin()); +} + +} // namespace dom +} // namespace mozilla diff --git a/layout/style/FontFace.h b/layout/style/FontFace.h new file mode 100644 index 000000000..374aa8e3f --- /dev/null +++ b/layout/style/FontFace.h @@ -0,0 +1,271 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_FontFace_h +#define mozilla_dom_FontFace_h + +#include "mozilla/dom/FontFaceBinding.h" +#include "gfxUserFontSet.h" +#include "nsAutoPtr.h" +#include "nsCSSPropertyID.h" +#include "nsCSSValue.h" +#include "nsWrapperCache.h" + +class gfxFontFaceBufferSource; +class nsCSSFontFaceRule; + +namespace mozilla { +struct CSSFontFaceDescriptors; +namespace dom { +class FontFaceBufferSource; +struct FontFaceDescriptors; +class FontFaceSet; +class Promise; +class StringOrArrayBufferOrArrayBufferView; +} // namespace dom +} // namespace mozilla + +namespace mozilla { +namespace dom { + +class FontFace final : public nsISupports, + public nsWrapperCache +{ + friend class mozilla::dom::FontFaceBufferSource; + friend class Entry; + +public: + class Entry final : public gfxUserFontEntry { + friend class FontFace; + + public: + Entry(gfxUserFontSet* aFontSet, + const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint8_t aStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet* aUnicodeRanges, + uint8_t aFontDisplay) + : gfxUserFontEntry(aFontSet, aFontFaceSrcList, aWeight, aStretch, + aStyle, aFeatureSettings, aLanguageOverride, + aUnicodeRanges, aFontDisplay) {} + + virtual void SetLoadState(UserFontLoadState aLoadState) override; + virtual void GetUserFontSets(nsTArray& aResult) override; + const AutoTArray& GetFontFaces() { return mFontFaces; } + + protected: + // 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; + }; + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FontFace) + + nsISupports* GetParentObject() const { return mParent; } + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + static already_AddRefed + CreateForRule(nsISupports* aGlobal, FontFaceSet* aFontFaceSet, + nsCSSFontFaceRule* aRule); + + nsCSSFontFaceRule* GetRule() { return mRule; } + + void GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const; + + gfxUserFontEntry* CreateUserFontEntry(); + gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; } + void SetUserFontEntry(gfxUserFontEntry* aEntry); + + /** + * Returns whether this object is in the specified FontFaceSet. + */ + bool IsInFontFaceSet(FontFaceSet* aFontFaceSet) const; + + void AddFontFaceSet(FontFaceSet* aFontFaceSet); + void RemoveFontFaceSet(FontFaceSet* aFontFaceSet); + + FontFaceSet* 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 whether a valid family name was available. + */ + bool GetFamilyName(nsString& aResult); + + /** + * 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; + + /** + * Creates a gfxFontFaceBufferSource to represent the font data + * in this object. + */ + already_AddRefed CreateBufferSource(); + + /** + * 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); + + // Web IDL + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + const nsAString& aFamily, + const mozilla::dom::StringOrArrayBufferOrArrayBufferView& aSource, + const mozilla::dom::FontFaceDescriptors& aDescriptors, + ErrorResult& aRV); + + void GetFamily(nsString& aResult); + void SetFamily(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetStyle(nsString& aResult); + void SetStyle(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetWeight(nsString& aResult); + void SetWeight(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetStretch(nsString& aResult); + void SetStretch(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetUnicodeRange(nsString& aResult); + void SetUnicodeRange(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetVariant(nsString& aResult); + void SetVariant(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetFeatureSettings(nsString& aResult); + void SetFeatureSettings(const nsAString& aValue, mozilla::ErrorResult& aRv); + void GetDisplay(nsString& aResult); + void SetDisplay(const nsAString& aValue, mozilla::ErrorResult& aRv); + + mozilla::dom::FontFaceLoadStatus Status(); + mozilla::dom::Promise* Load(mozilla::ErrorResult& aRv); + mozilla::dom::Promise* GetLoaded(mozilla::ErrorResult& aRv); + +private: + FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet); + ~FontFace(); + + void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource); + + // Helper function for Load. + void DoLoad(); + + /** + * Parses a @font-face descriptor value, storing the result in aResult. + * Returns whether the parsing was successful. + */ + bool ParseDescriptor(nsCSSFontDesc aDescID, const nsAString& aString, + nsCSSValue& aResult); + + // Helper function for the descriptor setter methods. + void SetDescriptor(nsCSSFontDesc aFontDesc, + const nsAString& aValue, + mozilla::ErrorResult& aRv); + + /** + * Sets all of the descriptor values in mDescriptors using values passed + * to the JS constructor. + */ + bool SetDescriptors(const nsAString& aFamily, + const FontFaceDescriptors& aDescriptors); + + /** + * Sets the current loading status. + */ + void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus); + + void GetDesc(nsCSSFontDesc aDescID, + nsCSSPropertyID aPropID, + nsString& aResult) const; + + /** + * Returns and takes ownership of the buffer storing the font data. + */ + void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength); + + // Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded + // if it doesn't already exist. + void Reject(nsresult aResult); + + // Creates mLoaded if it doesn't already exist. It may immediately resolve or + // reject mLoaded based on mStatus and mLoadedRejection. + void EnsurePromise(); + + nsCOMPtr mParent; + + // 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; + + // 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. + mozilla::dom::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. + uint8_t* mSourceBuffer; + uint32_t mSourceBufferLength; + + // 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. + nsAutoPtr mDescriptors; + + // 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 this FontFace appears in mFontFaceSet. + bool mInFontFaceSet; +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_FontFace_h) diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp new file mode 100644 index 000000000..59626fba4 --- /dev/null +++ b/layout/style/FontFaceSet.cpp @@ -0,0 +1,1848 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=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 "mozilla/css/Declaration.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/dom/FontFaceSetIterator.h" +#include "mozilla/dom/FontFaceSetLoadEvent.h" +#include "mozilla/dom/FontFaceSetLoadEventBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/SizePrintfMacros.h" +#include "mozilla/Sprintf.h" +#include "mozilla/Telemetry.h" +#include "nsAutoPtr.h" +#include "nsContentPolicyUtils.h" +#include "nsCSSParser.h" +#include "nsDeviceContext.h" +#include "nsFontFaceLoader.h" +#include "nsIConsoleService.h" +#include "nsIContentPolicy.h" +#include "nsIContentSecurityPolicy.h" +#include "nsIDocShell.h" +#include "nsIDocument.h" +#include "nsINetworkPredictor.h" +#include "nsIPresShell.h" +#include "nsIPrincipal.h" +#include "nsISupportsPriority.h" +#include "nsIWebNavigation.h" +#include "nsNetUtil.h" +#include "nsIProtocolHandler.h" +#include "nsIInputStream.h" +#include "nsPresContext.h" +#include "nsPrintfCString.h" +#include "nsStyleSet.h" +#include "nsUTF8Utils.h" +#include "nsDOMNavigationTiming.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) + +#define FONT_LOADING_API_ENABLED_PREF "layout.css.font-loading-api.enabled" + +NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); + 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); + } + if (tmp->mUserFontSet) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserFontSet->mFontFaceSet); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper) + tmp->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); + 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); + } + if (tmp->mUserFontSet) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet->mFontFaceSet); + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FontFaceSet) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +FontFaceSet::FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument) + : DOMEventTargetHelper(aWindow) + , mDocument(aDocument) + , mResolveLazilyCreatedReadyPromise(false) + , mStatus(FontFaceSetLoadStatus::Loaded) + , mNonRuleFacesDirty(false) + , mHasLoadingFontFaces(false) + , mHasLoadingFontFacesIsDirty(false) + , mDelayedLoadCheck(false) +{ + MOZ_COUNT_CTOR(FontFaceSet); + + nsCOMPtr global = do_QueryInterface(aWindow); + + // If the pref is not set, don't create the Promise (which the page wouldn't + // be able to get to anyway) as it causes the window.FontFaceSet constructor + // to be created. + if (global && PrefEnabled()) { + mResolveLazilyCreatedReadyPromise = true; + } + + if (!mDocument->DidFireDOMContentLoaded()) { + mDocument->AddSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"), + this, false, false); + } + + mDocument->CSSLoader()->AddObserver(this); + + mUserFontSet = new UserFontSet(this); +} + +FontFaceSet::~FontFaceSet() +{ + MOZ_COUNT_DTOR(FontFaceSet); + + Disconnect(); + for (auto it = mLoaders.Iter(); !it.Done(); it.Next()) { + it.Get()->GetKey()->Cancel(); + } +} + +JSObject* +FontFaceSet::WrapObject(JSContext* aContext, JS::Handle aGivenProto) +{ + return FontFaceSetBinding::Wrap(aContext, this, aGivenProto); +} + +void +FontFaceSet::Disconnect() +{ + RemoveDOMContentLoadedListener(); + + if (mDocument && mDocument->CSSLoader()) { + // We're null checking CSSLoader() since FontFaceSet::Disconnect() might be + // being called during unlink, at which time the loader amy already have + // been unlinked from the document. + mDocument->CSSLoader()->RemoveObserver(this); + } +} + +void +FontFaceSet::RemoveDOMContentLoadedListener() +{ + if (mDocument) { + mDocument->RemoveSystemEventListener(NS_LITERAL_STRING("DOMContentLoaded"), + this, false); + } +} + +void +FontFaceSet::ParseFontShorthandForMatching( + const nsAString& aFont, + RefPtr& aFamilyList, + uint32_t& aWeight, + int32_t& aStretch, + uint8_t& aStyle, + ErrorResult& aRv) +{ + // Parse aFont as a 'font' property value. + RefPtr declaration = new Declaration; + declaration->InitializeEmpty(); + + bool changed = false; + nsCSSParser parser; + parser.ParseProperty(eCSSProperty_font, + aFont, + mDocument->GetDocumentURI(), + mDocument->GetDocumentURI(), + mDocument->NodePrincipal(), + declaration, + &changed, + /* aIsImportant */ false); + + // All of the properties we are interested in should have been set at once. + MOZ_ASSERT(changed == (declaration->HasProperty(eCSSProperty_font_family) && + declaration->HasProperty(eCSSProperty_font_style) && + declaration->HasProperty(eCSSProperty_font_weight) && + declaration->HasProperty(eCSSProperty_font_stretch))); + + if (!changed) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + + nsCSSCompressedDataBlock* data = declaration->GetNormalBlock(); + MOZ_ASSERT(!declaration->GetImportantBlock()); + + const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family); + if (family->GetUnit() != eCSSUnit_FontFamilyList) { + // We got inherit, initial, unset, a system font, or a token stream. + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return; + } + + aFamilyList = + static_cast(family->GetFontFamilyListValue()); + + int32_t weight = data->ValueFor(eCSSProperty_font_weight)->GetIntValue(); + + // Resolve relative font weights against the initial of font-weight + // (normal, which is equivalent to 400). + if (weight == NS_STYLE_FONT_WEIGHT_BOLDER) { + weight = NS_FONT_WEIGHT_BOLD; + } else if (weight == NS_STYLE_FONT_WEIGHT_LIGHTER) { + weight = NS_FONT_WEIGHT_THIN; + } + + aWeight = weight; + + aStretch = data->ValueFor(eCSSProperty_font_stretch)->GetIntValue(); + aStyle = data->ValueFor(eCSSProperty_font_style)->GetIntValue(); +} + +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 +FontFaceSet::FindMatchingFontFaces(const nsAString& aFont, + const nsAString& aText, + nsTArray& aFontFaces, + ErrorResult& aRv) +{ + RefPtr familyList; + uint32_t weight; + int32_t stretch; + uint8_t italicStyle; + ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle, + aRv); + if (aRv.Failed()) { + return; + } + + gfxFontStyle style; + style.style = italicStyle; + style.weight = weight; + style.stretch = stretch; + + nsTArray* arrays[2]; + arrays[0] = &mNonRuleFaces; + arrays[1] = &mRuleFaces; + + // Set of FontFaces that we want to return. + nsTHashtable> matchingFaces; + + for (const FontFamilyName& fontFamilyName : familyList->GetFontlist()) { + RefPtr family = + mUserFontSet->LookupFamily(fontFamilyName.mName); + + if (!family) { + continue; + } + + AutoTArray entries; + bool needsBold; + family->FindAllFontsForStyle(style, entries, needsBold); + + for (gfxFontEntry* e : entries) { + FontFace::Entry* entry = static_cast(e); + if (HasAnyCharacterInUnicodeRange(entry, aText)) { + for (FontFace* f : entry->GetFontFaces()) { + matchingFaces.PutEntry(f); + } + } + } + } + + // Add all FontFaces in matchingFaces to aFontFaces, in the order + // they appear in the FontFaceSet. + for (nsTArray* array : arrays) { + for (FontFaceRecord& record : *array) { + FontFace* f = record.mFontFace; + if (matchingFaces.Contains(f)) { + aFontFaces.AppendElement(f); + } + } + } +} + +TimeStamp +FontFaceSet::GetNavigationStartTimeStamp() +{ + TimeStamp navStart; + RefPtr timing(mDocument->GetNavigationTiming()); + if (timing) { + navStart = timing->GetNavigationStartTimeStamp(); + } + return navStart; +} + +already_AddRefed +FontFaceSet::Load(JSContext* aCx, + const nsAString& aFont, + const nsAString& aText, + ErrorResult& aRv) +{ + FlushUserFontSet(); + + nsTArray> promises; + + nsTArray faces; + 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; + } + } + + nsIGlobalObject* globalObject = GetParentObject(); + if (!globalObject) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + JS::Rooted jsGlobal(aCx, globalObject->GetGlobalJSObject()); + GlobalObject global(aCx, jsGlobal); + + RefPtr result = Promise::All(global, promises, aRv); + return result.forget(); +} + +bool +FontFaceSet::Check(const nsAString& aFont, + const nsAString& aText, + ErrorResult& aRv) +{ + FlushUserFontSet(); + + nsTArray faces; + FindMatchingFontFaces(aFont, aText, faces, aRv); + if (aRv.Failed()) { + return false; + } + + for (FontFace* f : faces) { + if (f->Status() != FontFaceLoadStatus::Loaded) { + return false; + } + } + + return true; +} + +Promise* +FontFaceSet::GetReady(ErrorResult& aRv) +{ + 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; + } + } + + FlushUserFontSet(); + return mReady; +} + +FontFaceSetLoadStatus +FontFaceSet::Status() +{ + FlushUserFontSet(); + return mStatus; +} + +#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 + +FontFaceSet* +FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) +{ + FlushUserFontSet(); + + if (aFontFace.IsInFontFaceSet(this)) { + return this; + } + + if (aFontFace.HasRule()) { + aRv.Throw(NS_ERROR_DOM_INVALID_MODIFICATION_ERR); + return nullptr; + } + + 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->mSheetType = SheetType::Unknown; // unused for mNonRuleFaces + rec->mLoadEventShouldFire = + aFontFace.Status() == FontFaceLoadStatus::Unloaded || + aFontFace.Status() == FontFaceLoadStatus::Loading; + + mNonRuleFacesDirty = true; + RebuildUserFontSet(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingStarted(); + return this; +} + +void +FontFaceSet::Clear() +{ + FlushUserFontSet(); + + if (mNonRuleFaces.IsEmpty()) { + return; + } + + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + FontFace* f = mNonRuleFaces[i].mFontFace; + f->RemoveFontFaceSet(this); + } + + mNonRuleFaces.Clear(); + mNonRuleFacesDirty = true; + RebuildUserFontSet(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingFinished(); +} + +bool +FontFaceSet::Delete(FontFace& aFontFace) +{ + 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; + RebuildUserFontSet(); + mHasLoadingFontFacesIsDirty = true; + CheckLoadingFinished(); + return true; +} + +bool +FontFaceSet::HasAvailableFontFace(FontFace* aFontFace) +{ + return aFontFace->IsInFontFaceSet(this); +} + +bool +FontFaceSet::Has(FontFace& aFontFace) +{ + FlushUserFontSet(); + + return HasAvailableFontFace(&aFontFace); +} + +FontFace* +FontFaceSet::GetFontFaceAt(uint32_t aIndex) +{ + FlushUserFontSet(); + + if (aIndex < mRuleFaces.Length()) { + return mRuleFaces[aIndex].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 = 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 < Size(); i++) { + FontFace* face = GetFontFaceAt(i); + aCallback.Call(thisArg, *face, *face, *this, aRv); + if (aRv.Failed()) { + return; + } + } +} + +void +FontFaceSet::RemoveLoader(nsFontFaceLoader* aLoader) +{ + mLoaders.RemoveEntry(aLoader); +} + +nsresult +FontFaceSet::StartLoad(gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc) +{ + nsresult rv; + + nsCOMPtr streamLoader; + nsCOMPtr loadGroup(mDocument->GetDocumentLoadGroup()); + + 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), + aFontFaceSrc->mURI, + mDocument, + aUserFontEntry->GetPrincipal(), + nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS, + nsIContentPolicy::TYPE_FONT, + loadGroup); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr fontLoader = + new nsFontFaceLoader(aUserFontEntry, aFontFaceSrc->mURI, this, channel); + + if (LOG_ENABLED()) { + LOG(("userfonts (%p) download start - font uri: (%s) " + "referrer uri: (%s)\n", + fontLoader.get(), aFontFaceSrc->mURI->GetSpecOrDefault().get(), + aFontFaceSrc->mReferrer + ? aFontFaceSrc->mReferrer->GetSpecOrDefault().get() + : "")); + } + + nsCOMPtr httpChannel(do_QueryInterface(channel)); + if (httpChannel) { + httpChannel->SetReferrerWithPolicy(aFontFaceSrc->mReferrer, + mDocument->GetReferrerPolicy()); + nsAutoCString accept("application/font-woff;q=0.9,*/*;q=0.8"); + if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED)) { + accept.Insert(NS_LITERAL_CSTRING("application/font-woff2;q=1.0,"), 0); + } + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), + accept, false); + // For WOFF and WOFF2, we should tell servers/proxies/etc NOT to try + // and apply additional compression at the content-encoding layer + if (aFontFaceSrc->mFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF | + gfxUserFontSet::FLAG_FORMAT_WOFF2)) { + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"), + NS_LITERAL_CSTRING("identity"), false); + } + } + nsCOMPtr priorityChannel(do_QueryInterface(channel)); + if (priorityChannel) { + priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGH); + } + + rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader); + NS_ENSURE_SUCCESS(rv, rv); + + mozilla::net::PredictorLearn(aFontFaceSrc->mURI, mDocument->GetDocumentURI(), + nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, + loadGroup); + + rv = channel->AsyncOpen2(streamLoader); + if (NS_FAILED(rv)) { + fontLoader->DropChannel(); // explicitly need to break ref cycle + } + + if (NS_SUCCEEDED(rv)) { + mLoaders.PutEntry(fontLoader); + fontLoader->StartedLoading(streamLoader); + aUserFontEntry->SetLoader(fontLoader); // let the font entry remember the + // loader, in case we need to cancel it + } + + return rv; +} + +bool +FontFaceSet::UpdateRules(const nsTArray& aRules) +{ + MOZ_ASSERT(mUserFontSet); + + // 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 + nsDataHashtable, FontFace*> ruleFaceMap; + for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) { + FontFace* f = mRuleFaces[i].mFontFace; + if (!f) { + continue; + } + ruleFaceMap.Put(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; + mRuleFaces.SwapElements(oldRecords); + + // 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 (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) { + it.Data()->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. + nsTHashtable> 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. + if (handledRules.Contains(aRules[i].mRule)) { + continue; + } + nsCSSFontFaceRule* rule = aRules[i].mRule; + RefPtr f = ruleFaceMap.Get(rule); + if (!f.get()) { + f = FontFace::CreateForRule(GetParentObject(), this, rule); + } + InsertRuleFontFace(f, aRules[i].mSheetType, oldRecords, modified); + handledRules.PutEntry(aRules[i].mRule); + } + + 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, modified); + } + + // 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 = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) { + if (it.Data()->GetFontList().IsEmpty()) { + 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 (mUserFontSet->mRebuildLocalRules) { + mUserFontSet->mLocalRulesUsed = false; + mUserFontSet->mRebuildLocalRules = false; + } + + if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) { + LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", + mUserFontSet.get(), + (modified ? "modified" : "not modified"), + (int)(mRuleFaces.Length()))); + } + + return modified; +} + +static bool +HasLocalSrc(const nsCSSValue::Array *aSrcArr) +{ + size_t numSrc = aSrcArr->Count(); + for (size_t i = 0; i < numSrc; i++) { + if (aSrcArr->Item(i).GetUnit() == eCSSUnit_Local_Font) { + return true; + } + } + return false; +} + +void +FontFaceSet::IncrementGeneration(bool aIsRebuild) +{ + MOZ_ASSERT(mUserFontSet); + mUserFontSet->IncrementGeneration(aIsRebuild); +} + +void +FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace, + bool& aFontSetModified) +{ + nsAutoString fontfamily; + if (!aFontFace->GetFamilyName(fontfamily)) { + // If there is no family name, this rule cannot contribute a + // usable font, so there is no point in processing it further. + return; + } + + // Just create a new font entry if we haven't got one already. + if (!aFontFace->GetUserFontEntry()) { + // XXX Should we be checking mUserFontSet->mLocalRulesUsed like + // InsertRuleFontFace does? + RefPtr entry = + FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, + SheetType::Doc); + if (!entry) { + return; + } + aFontFace->SetUserFontEntry(entry); + } + + aFontSetModified = true; + mUserFontSet->AddUserFontEntry(fontfamily, aFontFace->GetUserFontEntry()); +} + +void +FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType, + nsTArray& aOldRecords, + bool& aFontSetModified) +{ + nsAutoString fontfamily; + if (!aFontFace->GetFamilyName(fontfamily)) { + // 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.mSheetType == aSheetType) { + + // if local rules were used, don't use the old font entry + // for rules containing src local usage + if (mUserFontSet->mLocalRulesUsed && + mUserFontSet->mRebuildLocalRules) { + nsCSSValue val; + aFontFace->GetDesc(eCSSFontDesc_Src, val); + nsCSSUnit unit = val.GetUnit(); + if (unit == eCSSUnit_Array && HasLocalSrc(val.GetArrayValue())) { + // 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"); + + mUserFontSet->AddUserFontEntry(fontfamily, entry); + + MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace), + "FontFace should not occur in mRuleFaces twice"); + + mRuleFaces.AppendElement(rec); + aOldRecords.RemoveElementAt(i); + // 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: + RefPtr entry = + FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, 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.mSheetType = aSheetType; + rec.mLoadEventShouldFire = + aFontFace->Status() == FontFaceLoadStatus::Unloaded || + aFontFace->Status() == FontFaceLoadStatus::Loading; + + aFontFace->SetUserFontEntry(entry); + + MOZ_ASSERT(!HasRuleFontFace(aFontFace), + "FontFace should not occur in mRuleFaces twice"); + + mRuleFaces.AppendElement(rec); + + // 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. + mUserFontSet->AddUserFontEntry(fontfamily, entry); +} + +/* static */ already_AddRefed +FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace) +{ + nsAutoString fontfamily; + if (!aFontFace->GetFamilyName(fontfamily)) { + // If there is no family name, this rule cannot contribute a + // usable font, so there is no point in processing it further. + return nullptr; + } + + return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, + SheetType::Doc); +} + +/* static */ already_AddRefed +FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName, + FontFace* aFontFace, + SheetType aSheetType) +{ + FontFaceSet* set = aFontFace->GetPrimaryFontFaceSet(); + + nsCSSValue val; + nsCSSUnit unit; + + uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL; + int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL; + uint8_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL; + uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE; + uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO; + + // set up weight + aFontFace->GetDesc(eCSSFontDesc_Weight, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) { + weight = val.GetIntValue(); + if (weight == 0) { + weight = NS_STYLE_FONT_WEIGHT_NORMAL; + } + } else if (unit == eCSSUnit_Normal) { + weight = NS_STYLE_FONT_WEIGHT_NORMAL; + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face weight has unexpected unit"); + } + + // set up stretch + aFontFace->GetDesc(eCSSFontDesc_Stretch, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Enumerated) { + stretch = val.GetIntValue(); + } else if (unit == eCSSUnit_Normal) { + stretch = NS_STYLE_FONT_STRETCH_NORMAL; + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face stretch has unexpected unit"); + } + + // set up font style + aFontFace->GetDesc(eCSSFontDesc_Style, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Enumerated) { + italicStyle = val.GetIntValue(); + } else if (unit == eCSSUnit_Normal) { + italicStyle = NS_STYLE_FONT_STYLE_NORMAL; + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face style has unexpected unit"); + } + + // set up font display + aFontFace->GetDesc(eCSSFontDesc_Display, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Enumerated) { + fontDisplay = val.GetIntValue(); + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face style has unexpected unit"); + } + + // set up font features + nsTArray featureSettings; + aFontFace->GetDesc(eCSSFontDesc_FontFeatureSettings, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Normal) { + // empty list of features + } else if (unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep) { + nsRuleNode::ComputeFontFeatures(val.GetPairListValue(), featureSettings); + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face font-feature-settings has unexpected unit"); + } + + // set up font language override + aFontFace->GetDesc(eCSSFontDesc_FontLanguageOverride, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Normal) { + // empty feature string + } else if (unit == eCSSUnit_String) { + nsString stringValue; + val.GetStringValue(stringValue); + languageOverride = gfxFontStyle::ParseFontLanguageOverride(stringValue); + } else { + NS_ASSERTION(unit == eCSSUnit_Null, + "@font-face font-language-override has unexpected unit"); + } + + // set up unicode-range + nsAutoPtr unicodeRanges; + aFontFace->GetDesc(eCSSFontDesc_UnicodeRange, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Array) { + unicodeRanges = new gfxCharacterMap(); + const nsCSSValue::Array& sources = *val.GetArrayValue(); + MOZ_ASSERT(sources.Count() % 2 == 0, + "odd number of entries in a unicode-range: array"); + + for (uint32_t i = 0; i < sources.Count(); i += 2) { + uint32_t min = sources[i].GetIntValue(); + uint32_t max = sources[i+1].GetIntValue(); + unicodeRanges->SetRange(min, max); + } + } + + // 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->CreateBufferSource(); + } else { + aFontFace->GetDesc(eCSSFontDesc_Src, val); + unit = val.GetUnit(); + if (unit == eCSSUnit_Array) { + nsCSSValue::Array* srcArr = val.GetArrayValue(); + size_t numSrc = srcArr->Count(); + + for (size_t i = 0; i < numSrc; i++) { + val = srcArr->Item(i); + unit = val.GetUnit(); + gfxFontFaceSrc* face = srcArray.AppendElements(1); + if (!face) + return nullptr; + + switch (unit) { + + case eCSSUnit_Local_Font: + val.GetStringValue(face->mLocalName); + face->mSourceType = gfxFontFaceSrc::eSourceType_Local; + face->mURI = nullptr; + face->mFormatFlags = 0; + break; + case eCSSUnit_URL: + face->mSourceType = gfxFontFaceSrc::eSourceType_URL; + face->mURI = val.GetURLValue(); + face->mReferrer = val.GetURLStructValue()->mReferrer; + face->mReferrerPolicy = set->mDocument->GetReferrerPolicy(); + face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal; + NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule"); + + // 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 + face->mUseOriginPrincipal = (aSheetType == SheetType::User || + aSheetType == SheetType::Agent); + + face->mLocalName.Truncate(); + face->mFormatFlags = 0; + while (i + 1 < numSrc && (val = srcArr->Item(i+1), + val.GetUnit() == eCSSUnit_Font_Format)) { + nsDependentString valueString(val.GetStringBufferValue()); + if (valueString.LowerCaseEqualsASCII("woff")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF; + } else if (Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) && + valueString.LowerCaseEqualsASCII("woff2")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF2; + } else if (valueString.LowerCaseEqualsASCII("opentype")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE; + } else if (valueString.LowerCaseEqualsASCII("truetype")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE; + } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT; + } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT; + } else if (valueString.LowerCaseEqualsASCII("svg")) { + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG; + } else { + // unknown format specified, mark to distinguish from the + // case where no format hints are specified + face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN; + } + i++; + } + if (!face->mURI) { + // if URI not valid, omit from src array + srcArray.RemoveElementAt(srcArray.Length() - 1); + NS_WARNING("null url in @font-face rule"); + continue; + } + break; + default: + NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL, + "strange unit type in font-face src array"); + break; + } + } + } else { + NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit"); + } + } + + if (srcArray.IsEmpty()) { + return nullptr; + } + + RefPtr entry = + set->mUserFontSet->FindOrCreateUserFontEntry(aFamilyName, srcArray, weight, + stretch, italicStyle, + featureSettings, + languageOverride, + unicodeRanges, fontDisplay); + return entry.forget(); +} + +nsCSSFontFaceRule* +FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry) +{ + NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries"); + for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { + FontFace* f = mRuleFaces[i].mFontFace; + gfxUserFontEntry* entry = f->GetUserFontEntry(); + if (entry && entry->GetPlatformFontEntry() == aFontEntry) { + return f->GetRule(); + } + } + return nullptr; +} + +nsCSSFontFaceRule* +FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry) +{ + for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { + FontFace* f = mRuleFaces[i].mFontFace; + if (f->GetUserFontEntry() == aUserFontEntry) { + return f->GetRule(); + } + } + return nullptr; +} + +nsresult +FontFaceSet::LogMessage(gfxUserFontEntry* aUserFontEntry, + const char* aMessage, + uint32_t aFlags, + nsresult aStatus) +{ + nsCOMPtr + console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (!console) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsAutoCString familyName; + nsAutoCString fontURI; + aUserFontEntry->GetFamilyNameAndURIForLogging(familyName, fontURI); + + char weightKeywordBuf[8]; // plenty to sprintf() a uint16_t + const char* weightKeyword; + const nsAFlatCString& weightKeywordString = + nsCSSProps::ValueToKeyword(aUserFontEntry->Weight(), + nsCSSProps::kFontWeightKTable); + if (weightKeywordString.Length() > 0) { + weightKeyword = weightKeywordString.get(); + } else { + SprintfLiteral(weightKeywordBuf, "%u", aUserFontEntry->Weight()); + weightKeyword = weightKeywordBuf; + } + + nsPrintfCString message + ("downloadable font: %s " + "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)", + aMessage, + familyName.get(), + aUserFontEntry->IsItalic() ? "italic" : "normal", + weightKeyword, + nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(), + nsCSSProps::kFontStretchKTable).get(), + aUserFontEntry->GetSrcIndex()); + + 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); + + if (LOG_ENABLED()) { + LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get())); + } + + // try to give the user an indication of where the rule came from + nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry); + nsString href; + nsString text; + nsresult rv; + uint32_t line = 0; + uint32_t column = 0; + if (rule) { + rv = rule->GetCssText(text); + NS_ENSURE_SUCCESS(rv, rv); + CSSStyleSheet* 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"); + } + line = rule->GetLineNumber(); + column = rule->GetColumnNumber(); + } + + nsCOMPtr scriptError = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t innerWindowID = mDocument->InnerWindowID(); + rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message), + href, // file + text, // src line + line, + column, + aFlags, // flags + "CSS Loader", // category (make separate?) + innerWindowID); + if (NS_SUCCEEDED(rv)) { + console->LogMessage(scriptError); + } + + return NS_OK; +} + +nsresult +FontFaceSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + nsIPrincipal** aPrincipal, + bool* aBypassCache) +{ + NS_ASSERTION(aFontFaceSrc && + aFontFaceSrc->mSourceType == gfxFontFaceSrc::eSourceType_URL, + "bad font face url passed to fontloader"); + + // check same-site origin + + NS_ASSERTION(aFontFaceSrc->mURI, "null font uri"); + if (!aFontFaceSrc->mURI) + return NS_ERROR_FAILURE; + + // use document principal, original principal if flag set + // this enables user stylesheets to load font files via + // @font-face rules + *aPrincipal = mDocument->NodePrincipal(); + + NS_ASSERTION(aFontFaceSrc->mOriginPrincipal, + "null origin principal in @font-face rule"); + if (aFontFaceSrc->mUseOriginPrincipal) { + *aPrincipal = aFontFaceSrc->mOriginPrincipal; + } + + *aBypassCache = false; + + nsCOMPtr docShell = mDocument->GetDocShell(); + if (docShell) { + uint32_t loadType; + if (NS_SUCCEEDED(docShell->GetLoadType(&loadType))) { + if ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) { + *aBypassCache = true; + } + } + uint32_t flags; + if (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags))) { + if (flags & nsIRequest::LOAD_BYPASS_CACHE) { + *aBypassCache = true; + } + } + } + + return NS_OK; +} + + +// @arg aPrincipal: generally this is mDocument->NodePrincipal() but +// might also be the original principal which enables user stylesheets +// to load font files via @font-face rules. +bool +FontFaceSet::IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal) +{ + int16_t shouldLoad = nsIContentPolicy::ACCEPT; + nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_FONT, + aFontLocation, + aPrincipal, + mDocument, + EmptyCString(), // mime type + nullptr, // aExtra + &shouldLoad, + nsContentUtils::GetContentPolicy(), + nsContentUtils::GetSecurityManager()); + + return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); +} + +nsresult +FontFaceSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength) +{ + nsresult rv; + + 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. + // Further, we only get here for data: loads, so it doesn't really matter + // whether we use SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS or not, to be more + // restrictive we use SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS. + rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel), + aFontFaceSrc->mURI, + mDocument, + aFontToLoad->GetPrincipal(), + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS, + nsIContentPolicy::TYPE_FONT); + + NS_ENSURE_SUCCESS(rv, rv); + + // blocking stream is OK for data URIs + nsCOMPtr stream; + rv = channel->Open2(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 (moz_xmalloc(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; +} + +bool +FontFaceSet::GetPrivateBrowsing() +{ + nsCOMPtr loadContext = mDocument->GetLoadContext(); + return loadContext && loadContext->UsePrivateBrowsing(); +} + +void +FontFaceSet::OnFontFaceStatusChanged(FontFace* aFontFace) +{ + 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; + nsCOMPtr checkTask = + NewRunnableMethod(this, &FontFaceSet::CheckLoadingFinishedAfterDelay); + NS_DispatchToMainThread(checkTask); + } + } +} + +void +FontFaceSet::DidRefresh() +{ + CheckLoadingFinished(); +} + +void +FontFaceSet::CheckLoadingFinishedAfterDelay() +{ + mDelayedLoadCheck = false; + CheckLoadingFinished(); +} + +void +FontFaceSet::CheckLoadingStarted() +{ + 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; + (new AsyncEventDispatcher(this, NS_LITERAL_STRING("loading"), + false))->PostDOMEvent(); + + if (PrefEnabled()) { + if (mReady) { + if (GetParentObject()) { + ErrorResult rv; + mReady = Promise::Create(GetParentObject(), rv); + } + } + if (!mReady) { + mResolveLazilyCreatedReadyPromise = false; + } + } +} + +void +FontFaceSet::UpdateHasLoadingFontFaces() +{ + mHasLoadingFontFacesIsDirty = false; + mHasLoadingFontFaces = false; + for (size_t i = 0; i < mRuleFaces.Length(); i++) { + FontFace* f = mRuleFaces[i].mFontFace; + if (f->Status() == FontFaceLoadStatus::Loading) { + mHasLoadingFontFaces = true; + return; + } + } + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) { + mHasLoadingFontFaces = true; + return; + } + } +} + +bool +FontFaceSet::HasLoadingFontFaces() +{ + if (mHasLoadingFontFacesIsDirty) { + UpdateHasLoadingFontFaces(); + } + return mHasLoadingFontFaces; +} + +bool +FontFaceSet::MightHavePendingFontLoads() +{ + // Check for FontFace objects in the FontFaceSet that are still loading. + if (HasLoadingFontFaces()) { + 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; +} + +void +FontFaceSet::CheckLoadingFinished() +{ + if (mDelayedLoadCheck) { + // Wait until the runnable posted in OnFontFaceStatusChanged calls us. + return; + } + + if (mStatus == FontFaceSetLoadStatus::Loaded) { + // We've already resolved mReady and dispatched the loadingdone/loadingerror + // events. + return; + } + + if (MightHavePendingFontLoads()) { + // We're not finished loading yet. + return; + } + + mStatus = FontFaceSetLoadStatus::Loaded; + if (mReady) { + mReady->MaybeResolve(this); + } else { + mResolveLazilyCreatedReadyPromise = true; + } + + // Now dispatch the loadingdone/loadingerror events. + nsTArray loaded; + nsTArray failed; + + for (size_t i = 0; i < mRuleFaces.Length(); i++) { + if (!mRuleFaces[i].mLoadEventShouldFire) { + continue; + } + FontFace* f = mRuleFaces[i].mFontFace; + if (f->Status() == FontFaceLoadStatus::Loaded) { + loaded.AppendElement(f); + mRuleFaces[i].mLoadEventShouldFire = false; + } else if (f->Status() == FontFaceLoadStatus::Error) { + failed.AppendElement(f); + mRuleFaces[i].mLoadEventShouldFire = false; + } + } + + for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { + if (!mNonRuleFaces[i].mLoadEventShouldFire) { + continue; + } + FontFace* f = mNonRuleFaces[i].mFontFace; + if (f->Status() == FontFaceLoadStatus::Loaded) { + loaded.AppendElement(f); + mNonRuleFaces[i].mLoadEventShouldFire = false; + } else if (f->Status() == FontFaceLoadStatus::Error) { + failed.AppendElement(f); + mNonRuleFaces[i].mLoadEventShouldFire = false; + } + } + + DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingdone"), loaded); + + if (!failed.IsEmpty()) { + DispatchLoadingFinishedEvent(NS_LITERAL_STRING("loadingerror"), failed); + } +} + +void +FontFaceSet::DispatchLoadingFinishedEvent( + const nsAString& aType, + const nsTArray& aFontFaces) +{ + FontFaceSetLoadEventInit init; + init.mBubbles = false; + init.mCancelable = false; + OwningNonNull* elements = + init.mFontfaces.AppendElements(aFontFaces.Length(), fallible); + MOZ_ASSERT(elements); + for (size_t i = 0; i < aFontFaces.Length(); i++) { + elements[i] = aFontFaces[i]; + } + RefPtr event = + FontFaceSetLoadEvent::Constructor(this, aType, init); + (new AsyncEventDispatcher(this, event))->PostDOMEvent(); +} + +// nsIDOMEventListener + +NS_IMETHODIMP +FontFaceSet::HandleEvent(nsIDOMEvent* aEvent) +{ + nsString type; + aEvent->GetType(type); + + if (!type.EqualsLiteral("DOMContentLoaded")) { + return NS_ERROR_FAILURE; + } + + RemoveDOMContentLoadedListener(); + CheckLoadingFinished(); + + return NS_OK; +} + +/* static */ bool +FontFaceSet::PrefEnabled() +{ + static bool initialized = false; + static bool enabled; + if (!initialized) { + initialized = true; + Preferences::AddBoolVarCache(&enabled, FONT_LOADING_API_ENABLED_PREF); + } + return enabled; +} + +// nsICSSLoaderObserver + +NS_IMETHODIMP +FontFaceSet::StyleSheetLoaded(StyleSheet* aSheet, + bool aWasAlternate, + nsresult aStatus) +{ + CheckLoadingFinished(); + return NS_OK; +} + +void +FontFaceSet::FlushUserFontSet() +{ + if (mDocument) { + mDocument->FlushUserFontSet(); + } +} + +void +FontFaceSet::RebuildUserFontSet() +{ + if (mDocument) { + mDocument->RebuildUserFontSet(); + } +} + +nsPresContext* +FontFaceSet::GetPresContext() +{ + if (!mDocument) { + return nullptr; + } + + nsIPresShell* shell = mDocument->GetShell(); + return shell ? shell->GetPresContext() : nullptr; +} + +// -- FontFaceSet::UserFontSet ------------------------------------------------ + +/* virtual */ nsresult +FontFaceSet::UserFontSet::CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + nsIPrincipal** aPrincipal, + bool* aBypassCache) +{ + if (!mFontFaceSet) { + return NS_ERROR_FAILURE; + } + return mFontFaceSet->CheckFontLoad(aFontFaceSrc, aPrincipal, aBypassCache); +} + +/* virtual */ bool +FontFaceSet::UserFontSet::IsFontLoadAllowed(nsIURI* aFontLocation, + nsIPrincipal* aPrincipal) +{ + return mFontFaceSet && + mFontFaceSet->IsFontLoadAllowed(aFontLocation, aPrincipal); +} + +/* virtual */ nsresult +FontFaceSet::UserFontSet::StartLoad(gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc) +{ + if (!mFontFaceSet) { + return NS_ERROR_FAILURE; + } + return mFontFaceSet->StartLoad(aUserFontEntry, aFontFaceSrc); +} + +void +FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize, + TimeStamp aDoneTime) +{ + mDownloadCount++; + mDownloadSize += aFontSize; + Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024); + + if (!mFontFaceSet) { + return; + } + + TimeStamp navStart = mFontFaceSet->GetNavigationStartTimeStamp(); + TimeStamp zero; + if (navStart != zero) { + Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START, + navStart, aDoneTime); + } +} + +/* virtual */ nsresult +FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry* aUserFontEntry, + const char* aMessage, + uint32_t aFlags, + nsresult aStatus) +{ + if (!mFontFaceSet) { + return NS_ERROR_FAILURE; + } + return mFontFaceSet->LogMessage(aUserFontEntry, aMessage, aFlags, aStatus); +} + +/* virtual */ nsresult +FontFaceSet::UserFontSet::SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength) +{ + if (!mFontFaceSet) { + return NS_ERROR_FAILURE; + } + return mFontFaceSet->SyncLoadFontData(aFontToLoad, aFontFaceSrc, + aBuffer, aBufferLength); +} + +/* virtual */ bool +FontFaceSet::UserFontSet::GetPrivateBrowsing() +{ + return mFontFaceSet && mFontFaceSet->GetPrivateBrowsing(); +} + +/* virtual */ void +FontFaceSet::UserFontSet::DoRebuildUserFontSet() +{ + if (!mFontFaceSet) { + return; + } + mFontFaceSet->RebuildUserFontSet(); +} + +/* virtual */ already_AddRefed +FontFaceSet::UserFontSet::CreateUserFontEntry( + const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint8_t aStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet* aUnicodeRanges, + uint8_t aFontDisplay) +{ + RefPtr entry = + new FontFace::Entry(this, aFontFaceSrcList, aWeight, aStretch, aStyle, + aFeatureSettings, aLanguageOverride, aUnicodeRanges, + aFontDisplay); + return entry.forget(); +} + +#undef LOG_ENABLED +#undef LOG diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h new file mode 100644 index 000000000..eafbd514e --- /dev/null +++ b/layout/style/FontFaceSet.h @@ -0,0 +1,356 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_FontFaceSet_h +#define mozilla_dom_FontFaceSet_h + +#include "mozilla/dom/FontFace.h" +#include "mozilla/dom/FontFaceSetBinding.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "gfxUserFontSet.h" +#include "nsCSSRules.h" +#include "nsICSSLoaderObserver.h" + +struct gfxFontFaceSrc; +class gfxUserFontEntry; +class nsFontFaceLoader; +class nsIPrincipal; +class nsPIDOMWindowInner; + +namespace mozilla { +namespace css { +class FontFamilyListRefCnt; +} // namespace css +namespace dom { +class FontFace; +class Promise; +} // namespace dom +} // namespace mozilla + +namespace mozilla { +namespace dom { + +class FontFaceSet final : public DOMEventTargetHelper + , public nsIDOMEventListener + , public nsICSSLoaderObserver +{ + friend class UserFontSet; + +public: + /** + * A gfxUserFontSet that integrates with the layout and style systems to + * manage @font-face rules and handle network requests for font loading. + * + * We would combine this class and FontFaceSet into the one class if it were + * possible; it's not because FontFaceSet is cycle collected and + * gfxUserFontSet isn't (and can't be, as gfx classes don't use the cycle + * collector). So UserFontSet exists just to override the needed virtual + * methods from gfxUserFontSet and to forward them on FontFaceSet. + */ + class UserFontSet final : public gfxUserFontSet + { + friend class FontFaceSet; + + public: + explicit UserFontSet(FontFaceSet* aFontFaceSet) + : mFontFaceSet(aFontFaceSet) + { + } + + FontFaceSet* GetFontFaceSet() { return mFontFaceSet; } + + virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + nsIPrincipal** aPrincipal, + bool* aBypassCache) override; + + virtual bool IsFontLoadAllowed(nsIURI* aFontLocation, + nsIPrincipal* aPrincipal) override; + + virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc) override; + + void RecordFontLoadDone(uint32_t aFontSize, + mozilla::TimeStamp aDoneTime) override; + + protected: + virtual bool GetPrivateBrowsing() override; + virtual nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength) override; + virtual nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, + const char* aMessage, + uint32_t aFlags = nsIScriptError::errorFlag, + nsresult aStatus = NS_OK) override; + virtual void DoRebuildUserFontSet() override; + virtual already_AddRefed CreateUserFontEntry( + const nsTArray& aFontFaceSrcList, + uint32_t aWeight, + int32_t aStretch, + uint8_t aStyle, + const nsTArray& aFeatureSettings, + uint32_t aLanguageOverride, + gfxSparseBitSet* aUnicodeRanges, + uint8_t aFontDisplay) override; + + private: + RefPtr mFontFaceSet; + }; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FontFaceSet, DOMEventTargetHelper) + NS_DECL_NSIDOMEVENTLISTENER + + FontFaceSet(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + UserFontSet* GetUserFontSet() { return mUserFontSet; } + + // Called by nsFontFaceLoader when the loader has completed normally. + // It's removed from the mLoaders set. + void RemoveLoader(nsFontFaceLoader* aLoader); + + bool UpdateRules(const nsTArray& aRules); + + nsPresContext* GetPresContext(); + + // search for @font-face rule that matches a platform font entry + nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry); + + void IncrementGeneration(bool aIsRebuild = false); + + /** + * 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(FontFace* aFontFace); + + /** + * Notification method called by a FontFace to indicate that its loading + * status has changed. + */ + void OnFontFaceStatusChanged(FontFace* aFontFace); + + /** + * Notification method called by the nsPresContext to indicate that the + * refresh driver ticked and flushed style and layout. + * were just flushed. + */ + void DidRefresh(); + + /** + * Returns whether the "layout.css.font-loading-api.enabled" pref is true. + */ + static bool PrefEnabled(); + + // nsICSSLoaderObserver + NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, + bool aWasAlternate, + nsresult aStatus) override; + + FontFace* GetFontFaceAt(uint32_t aIndex); + + void FlushUserFontSet(); + + static nsPresContext* GetPresContextFor(gfxUserFontSet* aUserFontSet) + { + FontFaceSet* set = static_cast(aUserFontSet)->mFontFaceSet; + return set ? set->GetPresContext() : nullptr; + } + + // -- Web IDL -------------------------------------------------------------- + + IMPL_EVENT_HANDLER(loading) + IMPL_EVENT_HANDLER(loadingdone) + IMPL_EVENT_HANDLER(loadingerror) + already_AddRefed Load(JSContext* aCx, + const nsAString& aFont, + const nsAString& aText, + mozilla::ErrorResult& aRv); + bool Check(const nsAString& aFont, + const nsAString& aText, + mozilla::ErrorResult& aRv); + mozilla::dom::Promise* GetReady(mozilla::ErrorResult& aRv); + mozilla::dom::FontFaceSetLoadStatus Status(); + + FontFaceSet* Add(FontFace& aFontFace, mozilla::ErrorResult& aRv); + void Clear(); + bool Delete(FontFace& aFontFace); + bool Has(FontFace& aFontFace); + uint32_t Size(); + already_AddRefed Entries(); + already_AddRefed Values(); + void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, + JS::Handle aThisArg, + mozilla::ErrorResult& aRv); + +private: + ~FontFaceSet(); + + /** + * Returns whether the given FontFace is currently "in" the FontFaceSet. + */ + bool HasAvailableFontFace(FontFace* aFontFace); + + /** + * Removes any listeners and observers. + */ + void Disconnect(); + + void RemoveDOMContentLoadedListener(); + + /** + * Returns whether there might be any pending font loads, which should cause + * the mReady Promise not to be resolved yet. + */ + bool MightHavePendingFontLoads(); + + /** + * Checks to see whether it is time to replace mReady and dispatch a + * "loading" event. + */ + void CheckLoadingStarted(); + + /** + * Checks to see whether it is time to resolve mReady and dispatch any + * "loadingdone" and "loadingerror" events. + */ + void CheckLoadingFinished(); + + /** + * Callback for invoking CheckLoadingFinished after going through the + * event loop. See OnFontFaceStatusChanged. + */ + void CheckLoadingFinishedAfterDelay(); + + /** + * Dispatches a FontFaceSetLoadEvent to this object. + */ + void DispatchLoadingFinishedEvent( + const nsAString& aType, + const nsTArray& aFontFaces); + + // Note: if you add new cycle collected objects to FontFaceRecord, + // make sure to update FontFaceSet's cycle collection macros + // accordingly. + struct FontFaceRecord { + RefPtr mFontFace; + SheetType mSheetType; // 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; + }; + + static already_AddRefed FindOrCreateUserFontEntryFromFontFace( + const nsAString& aFamilyName, + FontFace* aFontFace, + SheetType aSheetType); + + // search for @font-face rule that matches a userfont font entry + nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry); + + nsresult StartLoad(gfxUserFontEntry* aUserFontEntry, + const gfxFontFaceSrc* aFontFaceSrc); + nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc, + nsIPrincipal** aPrincipal, + bool* aBypassCache); + bool IsFontLoadAllowed(nsIURI* aFontLocation, nsIPrincipal* aPrincipal); + bool GetPrivateBrowsing(); + nsresult SyncLoadFontData(gfxUserFontEntry* aFontToLoad, + const gfxFontFaceSrc* aFontFaceSrc, + uint8_t*& aBuffer, + uint32_t& aBufferLength); + nsresult LogMessage(gfxUserFontEntry* aUserFontEntry, + const char* aMessage, + uint32_t aFlags, + nsresult aStatus); + void RebuildUserFontSet(); + + void InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType, + nsTArray& aOldRecords, + bool& aFontSetModified); + void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified); + +#ifdef DEBUG + bool HasRuleFontFace(FontFace* aFontFace); +#endif + + /** + * Returns whether we have any loading FontFace objects in the FontFaceSet. + */ + bool HasLoadingFontFaces(); + + // Helper function for HasLoadingFontFaces. + void UpdateHasLoadingFontFaces(); + + void ParseFontShorthandForMatching( + const nsAString& aFont, + RefPtr& aFamilyList, + uint32_t& aWeight, + int32_t& aStretch, + uint8_t& aStyle, + ErrorResult& aRv); + void FindMatchingFontFaces(const nsAString& aFont, + const nsAString& aText, + nsTArray& aFontFaces, + mozilla::ErrorResult& aRv); + + TimeStamp GetNavigationStartTimeStamp(); + + RefPtr mUserFontSet; + + // The document this is a FontFaceSet for. + nsCOMPtr mDocument; + + // 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; + + // 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< nsPtrHashKey > mLoaders; + + // 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; + + // The overall status of the loading or loaded fonts in the FontFaceSet. + mozilla::dom::FontFaceSetLoadStatus mStatus; + + // Whether mNonRuleFaces has changed since last time UpdateRules ran. + bool mNonRuleFacesDirty; + + // 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; + + // This variable is only valid when mLoadingDirty is false. + bool mHasLoadingFontFacesIsDirty; + + // Whether CheckLoadingFinished calls should be ignored. See comment in + // OnFontFaceStatusChanged. + bool mDelayedLoadCheck; +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_FontFaceSet_h) diff --git a/layout/style/FontFaceSetIterator.cpp b/layout/style/FontFaceSetIterator.cpp new file mode 100644 index 000000000..74997eb1a --- /dev/null +++ b/layout/style/FontFaceSetIterator.cpp @@ -0,0 +1,80 @@ +/* -*- 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/. */ + +#include "mozilla/dom/FontFaceSetIterator.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION(FontFaceSetIterator, mFontFaceSet) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(FontFaceSetIterator, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(FontFaceSetIterator, Release) + +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 FontFaceSetIteratorBinding::Wrap(aCx, this, aGivenProto, aReflector); +} + +void +FontFaceSetIterator::Next(JSContext* aCx, FontFaceSetIteratorResult& aResult, + ErrorResult& aRv) +{ + if (!mFontFaceSet) { + aResult.mDone = true; + return; + } + + FontFace* face = mFontFaceSet->GetFontFaceAt(mNextIndex++); + + 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::AutoValueArray<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 000000000..a764dfbf9 --- /dev/null +++ b/layout/style/FontFaceSetIterator.h @@ -0,0 +1,44 @@ +/* -*- 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/. */ + +#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(mozilla::dom::FontFaceSet* aFontFaceSet, + 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, + mozilla::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/GenerateCSSPropsGenerated.py b/layout/style/GenerateCSSPropsGenerated.py new file mode 100644 index 000000000..5038e9afe --- /dev/null +++ b/layout/style/GenerateCSSPropsGenerated.py @@ -0,0 +1,104 @@ +# 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 sys +import string +import argparse +import subprocess +import buildconfig +from mozbuild import shellutil + +def get_properties(preprocessorHeader): + cpp = list(buildconfig.substs['CPP']) + cpp += shellutil.split(buildconfig.substs['ACDEFINES']) + cpp.append(preprocessorHeader) + preprocessed = subprocess.check_output(cpp) + properties = [{"name":p[0], "prop":p[1], "id":p[2], + "flags":p[3], "pref":p[4], "proptype":p[5]} + for (i, p) in enumerate(eval(preprocessed))] + + # Sort the list so that longhand and logical properties are intermingled + # first, shorthand properties follow, then aliases appear last. This matches + # the order of the nsCSSPropertyID enum. + + def property_compare(x, y): + property_order = {"longhand": 0, "logical": 0, "shorthand": 1, "alias": 2} + return property_order[x["proptype"]] - property_order[y["proptype"]] + + properties = sorted(properties, cmp=property_compare) + + for i, p in enumerate(properties): + p["index"] = i + + # Record each property's IDL name. + for p in properties: + if "CSS_PROPERTY_INTERNAL" in p["flags"]: + p["idlname"] = None + else: + idl_name = p["prop"] + if not idl_name.startswith("Moz"): + idl_name = idl_name[0].lower() + idl_name[1:] + p["idlname"] = idl_name + + return properties + +def generate_idl_names(properties): + names = [] + for p in properties: + if p["proptype"] is "alias": + continue + if p["idlname"] is None: + names.append(" nullptr, // %s" % p["name"]) + else: + names.append(' "%s",' % p["idlname"]) + return "\n".join(names) + +def generate_assertions(properties): + def enum(p): + if p["proptype"] is "alias": + return "eCSSPropertyAlias_%s" % p["prop"] + else: + return "eCSSProperty_%s" % p["id"] + msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list ' + 'properties in nsCSSPropertyID order");') + return "\n".join(map(lambda p: msg % (enum(p), p["index"]), properties)) + +def generate_idl_name_positions(properties): + # Skip aliases. + ps = filter(lambda p: p["proptype"] is not "alias", properties) + + # Sort alphabetically by IDL name. + ps = sorted(ps, key=lambda p: p["idlname"]) + + # Annotate entries with the sorted position. + ps = [(p, position) for position, p in enumerate(ps)] + + # Sort back to nsCSSPropertyID order. + ps = sorted(ps, key=lambda (p, position): p["index"]) + + return ",\n".join(map(lambda (p, position): " %d" % position, ps)) + +def generate(output, cppTemplate, preprocessorHeader): + cppFile = open(cppTemplate, "r") + cppTemplate = cppFile.read() + cppFile.close() + + properties = get_properties(preprocessorHeader) + substitutions = { + "idl_names": generate_idl_names(properties), + "assertions": generate_assertions(properties), + "idl_name_positions": generate_idl_name_positions(properties), + } + output.write("/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n" + + string.Template(cppTemplate).substitute(substitutions) + "\n") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('cppTemplate', help='CSS property file template') + parser.add_argument('preprocessorHeader', help='Header file to pass through the preprocessor') + args = parser.parse_args() + generate(sys.stdout, args.cppTemplate, args.preprocessorHeader) + +if __name__ == '__main__': + main() diff --git a/layout/style/GroupRule.h b/layout/style/GroupRule.h new file mode 100644 index 000000000..ec781fae6 --- /dev/null +++ b/layout/style/GroupRule.h @@ -0,0 +1,101 @@ +/* -*- 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/. */ + +/* + * 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/IncrementalClearCOMRuleArray.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/css/Rule.h" +#include "nsCycleCollectionParticipant.h" + +class nsPresContext; +class nsMediaQueryResultCacheKey; + +namespace mozilla { + +class CSSStyleSheet; + +namespace css { + +class GroupRuleRuleList; + +// inherits from Rule so it can be shared between +// MediaRule and DocumentRule +class GroupRule : public Rule +{ +protected: + GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber); + GroupRule(const GroupRule& aCopy); + virtual ~GroupRule(); +public: + + NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + // implement part of Rule + DECL_STYLE_RULE_INHERIT_NO_DOMRULE +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + virtual void SetStyleSheet(CSSStyleSheet* aSheet) override; + +public: + void AppendStyleRule(Rule* aRule); + + int32_t StyleRuleCount() const { return mRules.Count(); } + Rule* GetStyleRuleAt(int32_t aIndex) const; + + typedef IncrementalClearCOMRuleArray::nsCOMArrayEnumFunc RuleEnumFunc; + bool EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const; + + /* + * The next three methods should never be called unless you have first + * called WillDirty() on the parent stylesheet. After they are + * called, DidDirty() needs to be called on the sheet. + */ + nsresult DeleteStyleRuleAt(uint32_t aIndex); + nsresult InsertStyleRuleAt(uint32_t aIndex, Rule* aRule); + + virtual bool UseForPresentation(nsPresContext* aPresContext, + nsMediaQueryResultCacheKey& aKey) = 0; + + // non-virtual -- it is only called by subclasses + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override = 0; + + static bool + CloneRuleInto(Rule* aRule, void* aArray) + { + RefPtr clone = aRule->Clone(); + static_cast(aArray)->AppendObject(clone); + return true; + } + +protected: + // to help implement nsIDOMCSSRule + void AppendRulesToCssText(nsAString& aCssText); + + // to implement common methods on nsIDOMCSSMediaRule and + // nsIDOMCSSMozDocumentRule + nsresult GetCssRules(nsIDOMCSSRuleList* *aRuleList); + nsresult InsertRule(const nsAString & aRule, uint32_t aIndex, + uint32_t* _retval); + nsresult DeleteRule(uint32_t aIndex); + + IncrementalClearCOMRuleArray mRules; + RefPtr mRuleCollection; // lazily constructed +}; + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_GroupRule_h__ */ diff --git a/layout/style/HandleRefPtr.h b/layout/style/HandleRefPtr.h new file mode 100644 index 000000000..0a73a4cf7 --- /dev/null +++ b/layout/style/HandleRefPtr.h @@ -0,0 +1,133 @@ +/* -*- 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/. + */ + +/* smart pointer for strong references to objects through pointer-like + * "handle" objects */ + +#include +#include "mozilla/Assertions.h" + +#ifndef mozilla_HandleRefPtr_h +#define mozilla_HandleRefPtr_h + +namespace mozilla { + +/** + * A class for holding strong references to handle-managed objects. + * + * This is intended for use with objects like RestyleManagerHandle, + * where the handle type is not a pointer but which can still have + * ->AddRef() and ->Release() called on it. + */ +template +class HandleRefPtr +{ +public: + HandleRefPtr() {} + + HandleRefPtr(HandleRefPtr& aRhs) + { + assign(aRhs.mHandle); + } + + HandleRefPtr(HandleRefPtr&& aRhs) + { + std::swap(mHandle, aRhs.mHandle); + } + + MOZ_IMPLICIT HandleRefPtr(T aRhs) + { + assign(aRhs); + } + + HandleRefPtr& operator=(HandleRefPtr& aRhs) + { + assign(aRhs.mHandle); + return *this; + } + + HandleRefPtr& operator=(T aRhs) + { + assign(aRhs); + return *this; + } + + ~HandleRefPtr() { assign(nullptr); } + + explicit operator bool() const { return !!mHandle; } + bool operator!() const { return !mHandle; } + + operator T() const { return mHandle; } + T operator->() const { return mHandle; } + + void swap(HandleRefPtr& aOther) + { + std::swap(mHandle, aOther.mHandle); + } + +private: + void assign(T aPtr) + { + // AddRef early so |aPtr| can't disappear underneath us. + if (aPtr) { + aPtr->AddRef(); + } + + // Don't release |mHandle| yet: if |mHandle| indirectly owns |this|, + // releasing would invalidate |this|. Swap |aPtr| for |mHandle| so that + // |aPtr| lives as long as |this|, then release the new |aPtr| (really the + // original |mHandle|) so that if |this| is invalidated, we're not using it + // any more. + std::swap(mHandle, aPtr); + + if (aPtr) { + aPtr->Release(); + } + } + + T mHandle; +}; + +template +inline bool operator==(const HandleRefPtr& aLHS, const HandleRefPtr& aRHS) +{ + return static_cast(aLHS) == static_cast(aRHS); +} + +template +inline bool operator==(const HandleRefPtr& aLHS, T aRHS) +{ + return static_cast(aLHS) == aRHS; +} + +template +inline bool operator==(T aLHS, const HandleRefPtr& aRHS) +{ + return aLHS == static_cast(aRHS); +} + +template +inline bool operator!=(const HandleRefPtr& aLHS, const HandleRefPtr& aRHS) +{ + return !(aLHS == aRHS); +} + +template +inline bool operator!=(const HandleRefPtr& aLHS, T aRHS) +{ + return !(aLHS == aRHS); +} + +template +inline bool operator!=(T aLHS, const HandleRefPtr& aRHS) +{ + return !(aLHS == aRHS); +} + +} // namespace mozilla + +#endif // mozilla_HandleRefPtr_h diff --git a/layout/style/ImageDocument.css b/layout/style/ImageDocument.css new file mode 100644 index 000000000..a79d2213f --- /dev/null +++ b/layout/style/ImageDocument.css @@ -0,0 +1,31 @@ +/* -*- 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. +*/ + +@media not print { + .overflowingVertical, .overflowingHorizontalOnly { + cursor: zoom-out; + } + + .shrinkToFit { + cursor: zoom-in; + } +} + +@media print { + /* We must declare the image as a block element. If we stay as + an inline element, our parent LineBox will be inline too and + ignore the available height during reflow. + This is bad during printing, it means tall image frames won't know + the size of the paper and cannot break into continuations along + multiple pages. */ + img { + display: block; + } +} diff --git a/layout/style/ImageLoader.cpp b/layout/style/ImageLoader.cpp new file mode 100644 index 000000000..0a605abc9 --- /dev/null +++ b/layout/style/ImageLoader.cpp @@ -0,0 +1,529 @@ +/* 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 "nsAutoPtr.h" +#include "nsContentUtils.h" +#include "nsLayoutUtils.h" +#include "nsError.h" +#include "nsDisplayList.h" +#include "FrameLayerBuilder.h" +#include "nsSVGEffects.h" +#include "imgIContainer.h" +#include "Image.h" + +namespace mozilla { +namespace css { + +void +ImageLoader::DropDocumentReference() +{ + // 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()); + + for (auto it = mImages.Iter(); !it.Done(); it.Next()) { + ImageLoader::Image* image = it.Get()->GetKey(); + imgIRequest* request = image->mRequests.GetWeak(mDocument); + if (request) { + request->CancelAndForgetObserver(NS_BINDING_ABORTED); + } + image->mRequests.Remove(mDocument); + } + mImages.Clear(); + + mDocument = nullptr; +} + +void +ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, + nsIFrame* aFrame) +{ + 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 == this); + + FrameSet* frameSet = nullptr; + if (mRequestToFrameMap.Get(aRequest, &frameSet)) { + NS_ASSERTION(frameSet, "This should never be null!"); + } + + if (!frameSet) { + nsAutoPtr newFrameSet(new FrameSet()); + + mRequestToFrameMap.Put(aRequest, newFrameSet); + frameSet = newFrameSet.forget(); + + nsPresContext* presContext = GetPresContext(); + if (presContext) { + nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, + aRequest, + nullptr); + } + } + + RequestSet* requestSet = nullptr; + if (mFrameToRequestMap.Get(aFrame, &requestSet)) { + NS_ASSERTION(requestSet, "This should never be null"); + } + + if (!requestSet) { + nsAutoPtr newRequestSet(new RequestSet()); + + mFrameToRequestMap.Put(aFrame, newRequestSet); + requestSet = newRequestSet.forget(); + } + + // Add these to the sets, but only if they're not already there. + uint32_t i = frameSet->IndexOfFirstElementGt(aFrame); + if (i == 0 || aFrame != frameSet->ElementAt(i-1)) { + frameSet->InsertElementAt(i, aFrame); + } + i = requestSet->IndexOfFirstElementGt(aRequest); + if (i == 0 || aRequest != requestSet->ElementAt(i-1)) { + requestSet->InsertElementAt(i, aRequest); + } +} + +void +ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage) +{ + NS_ASSERTION(aImage, "This should never be null!"); + + bool found = false; + aImage->mRequests.GetWeak(mDocument, &found); + if (found) { + // This document already has a request. + return; + } + + imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr); + if (!canonicalRequest) { + // The image was blocked or something. + return; + } + + RefPtr request; + + // Ignore errors here. If cloning fails for some reason we'll put a null + // entry in the hash and we won't keep trying to clone. + mInClone = true; + canonicalRequest->Clone(this, getter_AddRefs(request)); + mInClone = false; + + aImage->mRequests.Put(mDocument, request); + + AddImage(aImage); +} + +void +ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage) +{ + RemoveImage(aImage); +} + +void +ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest, + nsIFrame* aFrame) +{ + FrameSet* frameSet = nullptr; + RequestSet* requestSet = nullptr; + +#ifdef DEBUG + { + nsCOMPtr observer; + aRequest->GetNotificationObserver(getter_AddRefs(observer)); + MOZ_ASSERT(!observer || observer == this); + } +#endif + + mRequestToFrameMap.Get(aRequest, &frameSet); + mFrameToRequestMap.Get(aFrame, &requestSet); + + if (frameSet) { + frameSet->RemoveElementSorted(aFrame); + } + if (requestSet) { + requestSet->RemoveElementSorted(aRequest); + } + + if (frameSet && !frameSet->Length()) { + mRequestToFrameMap.Remove(aRequest); + + nsPresContext* presContext = GetPresContext(); + if (presContext) { + nsLayoutUtils::DeregisterImageRequest(presContext, + aRequest, + nullptr); + } + } + + if (requestSet && !requestSet->Length()) { + mFrameToRequestMap.Remove(aFrame); + } +} + +void +ImageLoader::DropRequestsForFrame(nsIFrame* aFrame) +{ + RequestSet* requestSet = nullptr; + if (!mFrameToRequestMap.Get(aFrame, &requestSet)) { + return; + } + + NS_ASSERTION(requestSet, "This should never be null"); + + RequestSet frozenRequestSet(*requestSet); + for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) { + imgIRequest* request = frozenRequestSet.ElementAt(i - 1); + + DisassociateRequestFromFrame(request, aFrame); + } +} + +void +ImageLoader::SetAnimationMode(uint16_t aMode) +{ + NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode || + aMode == imgIContainer::kDontAnimMode || + aMode == imgIContainer::kLoopOnceAnimMode, + "Wrong Animation Mode is being set!"); + + for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) { + auto request = static_cast(iter.Key()); + +#ifdef DEBUG + { + nsCOMPtr debugRequest = do_QueryInterface(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) +{ + for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) { + auto request = static_cast(iter.Key()); + +#ifdef DEBUG + { + nsCOMPtr debugRequest = do_QueryInterface(request); + NS_ASSERTION(debugRequest == request, "This is bad"); + } +#endif + + if (aPresContext) { + nsLayoutUtils::DeregisterImageRequest(aPresContext, + request, + nullptr); + } + } + + mRequestToFrameMap.Clear(); + mFrameToRequestMap.Clear(); +} + +void +ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal, + nsIURI* aReferrer, ImageLoader::Image* aImage) +{ + NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?"); + + aImage->mRequests.Put(nullptr, nullptr); + + if (!aURI) { + return; + } + + RefPtr request; + nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument, + aOriginPrincipal, aReferrer, + mDocument->GetReferrerPolicy(), + nullptr, nsIRequest::LOAD_NORMAL, + NS_LITERAL_STRING("css"), + getter_AddRefs(request)); + + if (NS_FAILED(rv) || !request) { + return; + } + + RefPtr clonedRequest; + mInClone = true; + rv = request->Clone(this, getter_AddRefs(clonedRequest)); + mInClone = false; + + if (NS_FAILED(rv)) { + return; + } + + aImage->mRequests.Put(nullptr, request); + aImage->mRequests.Put(mDocument, clonedRequest); + + AddImage(aImage); +} + +void +ImageLoader::AddImage(ImageLoader::Image* aImage) +{ + NS_ASSERTION(!mImages.Contains(aImage), "Huh?"); + mImages.PutEntry(aImage); +} + +void +ImageLoader::RemoveImage(ImageLoader::Image* aImage) +{ + NS_ASSERTION(mImages.Contains(aImage), "Huh?"); + mImages.RemoveEntry(aImage); +} + +nsPresContext* +ImageLoader::GetPresContext() +{ + if (!mDocument) { + return nullptr; + } + + nsIPresShell* shell = mDocument->GetShell(); + if (!shell) { + return nullptr; + } + + return shell->GetPresContext(); +} + +void InvalidateImagesCallback(nsIFrame* aFrame, + FrameLayerBuilder::DisplayItemData* aItem) +{ + nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey()); + uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type); + + if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) { + return; + } + + if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { + printf_stderr("Invalidating display item(type=%d) based on frame %p \ + because it might contain an invalidated image\n", type, aFrame); + } + aItem->Invalidate(); + aFrame->SchedulePaint(); +} + +void +ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) +{ + NS_ASSERTION(aFrameSet, "Must have a frame set"); + NS_ASSERTION(mDocument, "Should have returned earlier!"); + + FrameSet::size_type length = aFrameSet->Length(); + for (FrameSet::size_type i = 0; i < length; i++) { + nsIFrame* frame = aFrameSet->ElementAt(i); + + if (frame->StyleVisibility()->IsVisible()) { + if (frame->IsFrameOfType(nsIFrame::eTablePart)) { + // Tables don't necessarily build border/background display items + // for the individual table part frames, so IterateRetainedDataFor + // might not find the right display item. + frame->InvalidateFrame(); + } else { + FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback); + + // Update ancestor rendering observers (-moz-element etc) + nsIFrame *f = frame; + while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { + nsSVGEffects::InvalidateDirectRenderingObservers(f); + f = nsLayoutUtils::GetCrossDocParentFrame(f); + } + + if (aForcePaint) { + frame->SchedulePaint(); + } + } + } + } +} + +NS_IMPL_ADDREF(ImageLoader) +NS_IMPL_RELEASE(ImageLoader) + +NS_INTERFACE_MAP_BEGIN(ImageLoader) + NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) + NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) +{ + 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); + } + } + + return NS_OK; +} + +nsresult +ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) +{ + nsPresContext* presContext = GetPresContext(); + if (!presContext) { + return NS_OK; + } + + aImage->SetAnimationMode(presContext->ImageAnimationMode()); + + return NS_OK; +} + +nsresult +ImageLoader::OnImageIsAnimated(imgIRequest* aRequest) +{ + if (!mDocument) { + return NS_OK; + } + + FrameSet* frameSet = nullptr; + if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { + return NS_OK; + } + + // Register with the refresh driver now that we are aware that + // we are animated. + nsPresContext* presContext = GetPresContext(); + if (presContext) { + nsLayoutUtils::RegisterImageRequest(presContext, + aRequest, + nullptr); + } + + return NS_OK; +} + +nsresult +ImageLoader::OnFrameComplete(imgIRequest* aRequest) +{ + if (!mDocument || mInClone) { + return NS_OK; + } + + FrameSet* frameSet = nullptr; + if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { + return NS_OK; + } + + NS_ASSERTION(frameSet, "This should never be null!"); + + // 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). + DoRedraw(frameSet, /* aForcePaint = */ true); + + return NS_OK; +} + +nsresult +ImageLoader::OnFrameUpdate(imgIRequest* aRequest) +{ + if (!mDocument || mInClone) { + return NS_OK; + } + + FrameSet* frameSet = nullptr; + if (!mRequestToFrameMap.Get(aRequest, &frameSet)) { + return NS_OK; + } + + NS_ASSERTION(frameSet, "This should never be null!"); + + DoRedraw(frameSet, /* aForcePaint = */ false); + + return NS_OK; +} + +NS_IMETHODIMP +ImageLoader::BlockOnload(imgIRequest* aRequest) +{ + if (!mDocument) { + return NS_OK; + } + + mDocument->BlockOnload(); + + return NS_OK; +} + +NS_IMETHODIMP +ImageLoader::UnblockOnload(imgIRequest* aRequest) +{ + if (!mDocument) { + return NS_OK; + } + + mDocument->UnblockOnload(false); + + return NS_OK; +} + +void +ImageLoader::FlushUseCounters() +{ + for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { + nsPtrHashKey* key = iter.Get(); + ImageLoader::Image* image = key->GetKey(); + + imgIRequest* request = image->mRequests.GetWeak(mDocument); + + nsCOMPtr container; + request->GetImage(getter_AddRefs(container)); + if (container) { + static_cast(container.get())->ReportUseCounters(); + } + } +} + +} // namespace css +} // namespace mozilla diff --git a/layout/style/ImageLoader.h b/layout/style/ImageLoader.h new file mode 100644 index 000000000..4e29da5ed --- /dev/null +++ b/layout/style/ImageLoader.h @@ -0,0 +1,124 @@ +/* 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 "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsTArray.h" +#include "imgIRequest.h" +#include "imgIOnloadBlocker.h" +#include "imgINotificationObserver.h" +#include "mozilla/Attributes.h" + +class imgIContainer; +class nsIFrame; +class nsIDocument; +class nsPresContext; +class nsIURI; +class nsIPrincipal; + +namespace mozilla { +namespace css { + +struct ImageValue; + +class ImageLoader final : public imgINotificationObserver, + public imgIOnloadBlocker +{ +public: + typedef mozilla::css::ImageValue Image; + + explicit ImageLoader(nsIDocument* aDocument) + : mDocument(aDocument), + mInClone(false) + { + MOZ_ASSERT(mDocument); + } + + NS_DECL_ISUPPORTS + NS_DECL_IMGIONLOADBLOCKER + NS_DECL_IMGINOTIFICATIONOBSERVER + + void DropDocumentReference(); + + void MaybeRegisterCSSImage(Image* aImage); + void DeregisterCSSImage(Image* aImage); + + void AssociateRequestToFrame(imgIRequest* aRequest, + nsIFrame* aFrame); + + void DisassociateRequestFromFrame(imgIRequest* aRequest, + nsIFrame* aFrame); + + void DropRequestsForFrame(nsIFrame* aFrame); + + 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); + + void LoadImage(nsIURI* aURI, nsIPrincipal* aPrincipal, nsIURI* aReferrer, + Image* aCSSValue); + + void DestroyRequest(imgIRequest* aRequest); + + void FlushUseCounters(); + +private: + ~ImageLoader() {} + + // 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. + + typedef nsTArray FrameSet; + typedef nsTArray > RequestSet; + typedef nsTHashtable > ImageHashSet; + typedef nsClassHashtable RequestToFrameMap; + typedef nsClassHashtable, + RequestSet> FrameToRequestMap; + + void AddImage(Image* aCSSImage); + void RemoveImage(Image* aCSSImage); + + nsPresContext* GetPresContext(); + + void DoRedraw(FrameSet* aFrameSet, bool aForcePaint); + + nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage); + nsresult OnFrameComplete(imgIRequest* aRequest); + nsresult OnImageIsAnimated(imgIRequest* aRequest); + nsresult OnFrameUpdate(imgIRequest* aRequest); + + // 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. + nsIDocument* mDocument; + + // The set of all nsCSSValue::Images (whether they're associated a frame or + // not). We'll need this when we go away to remove any requests associated + // with our document from those Images. + ImageHashSet mImages; + + // Are we cloning? If so, ignore any notifications we get. + bool mInClone; +}; + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_ImageLoader_h___ */ diff --git a/layout/style/ImportRule.h b/layout/style/ImportRule.h new file mode 100644 index 000000000..f0803ba9d --- /dev/null +++ b/layout/style/ImportRule.h @@ -0,0 +1,72 @@ +/* -*- 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/. */ + +/* class for CSS @import rules */ + +#ifndef mozilla_css_ImportRule_h__ +#define mozilla_css_ImportRule_h__ + +#include "mozilla/Attributes.h" + +#include "mozilla/MemoryReporting.h" +#include "mozilla/css/Rule.h" +#include "nsIDOMCSSImportRule.h" + +class nsMediaList; +class nsString; + +namespace mozilla { + +class CSSStyleSheet; + +namespace css { + +class ImportRule final : public Rule, + public nsIDOMCSSImportRule +{ +public: + ImportRule(nsMediaList* aMedia, const nsString& aURLSpec, + uint32_t aLineNumber, uint32_t aColumnNumber); +private: + // for |Clone| + ImportRule(const ImportRule& aCopy); + ~ImportRule(); +public: + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, mozilla::css::Rule) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + DECL_STYLE_RULE_INHERIT + +#ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING + using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet +#endif + + // Rule methods +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + virtual int32_t GetType() const override; + virtual already_AddRefed Clone() const override; + + void SetSheet(CSSStyleSheet*); + + virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; + + // nsIDOMCSSRule interface + NS_DECL_NSIDOMCSSRULE + + // nsIDOMCSSImportRule interface + NS_DECL_NSIDOMCSSIMPORTRULE + +private: + nsString mURLSpec; + RefPtr mMedia; + RefPtr mChildSheet; +}; + +} // namespace css +} // namespace mozilla + +#endif /* mozilla_css_ImportRule_h__ */ diff --git a/layout/style/IncrementalClearCOMRuleArray.cpp b/layout/style/IncrementalClearCOMRuleArray.cpp new file mode 100644 index 000000000..92cdced22 --- /dev/null +++ b/layout/style/IncrementalClearCOMRuleArray.cpp @@ -0,0 +1,81 @@ +/* -*- 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/IncrementalClearCOMRuleArray.h" + +#include // For std::min +#include "nsCycleCollector.h" +#include "mozilla/DeferredFinalize.h" +#include "nsTArray.h" +#include "nsCCUncollectableMarker.h" + +using namespace mozilla; + +typedef nsCOMArray RuleArray; +typedef nsTArray RuleArrayArray; + + +// These methods are based on those in DeferredFinalizerImpl. + +static void* +AppendRulesArrayPointer(void* aData, void* aObject) +{ + RuleArrayArray* rulesArray = static_cast(aData); + RuleArray* oldRules = static_cast(aObject); + + if (!rulesArray) { + rulesArray = new RuleArrayArray(); + } + + RuleArray* newRules = rulesArray->AppendElement(); + newRules->SwapElements(*oldRules); + + return rulesArray; +} + +// Remove up to |aSliceBudget| css::Rules from the arrays, starting at +// the end of the last array. +static bool +DeferredFinalizeRulesArray(uint32_t aSliceBudget, void* aData) +{ + MOZ_ASSERT(aSliceBudget > 0, "nonsensical/useless call with aSliceBudget == 0"); + RuleArrayArray* rulesArray = static_cast(aData); + + size_t newOuterLen = rulesArray->Length(); + + while (aSliceBudget > 0 && newOuterLen > 0) { + RuleArray& lastArray = rulesArray->ElementAt(newOuterLen - 1); + uint32_t innerLen = lastArray.Length(); + uint32_t currSlice = std::min(innerLen, aSliceBudget); + uint32_t newInnerLen = innerLen - currSlice; + lastArray.TruncateLength(newInnerLen); + aSliceBudget -= currSlice; + if (newInnerLen == 0) { + newOuterLen -= 1; + } + } + + rulesArray->TruncateLength(newOuterLen); + + if (newOuterLen == 0) { + delete rulesArray; + return true; + } + return false; +} + +void +IncrementalClearCOMRuleArray::Clear() +{ + // Destroy the array incrementally if it is long and we + // haven't started shutting down. + if (Length() > 10 && nsCCUncollectableMarker::sGeneration) { + DeferredFinalize(AppendRulesArrayPointer, DeferredFinalizeRulesArray, this); + } else { + nsCOMArray::Clear(); + } + MOZ_ASSERT(Length() == 0); +} diff --git a/layout/style/IncrementalClearCOMRuleArray.h b/layout/style/IncrementalClearCOMRuleArray.h new file mode 100644 index 000000000..9934fd683 --- /dev/null +++ b/layout/style/IncrementalClearCOMRuleArray.h @@ -0,0 +1,28 @@ +/* -*- 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_IncrementalClearCOMRuleArray_h +#define mozilla_IncrementalClearCOMRuleArray_h + +#include "nsCOMArray.h" + +namespace mozilla { +namespace css { +class Rule; +} // namespace css + +class IncrementalClearCOMRuleArray : public nsCOMArray +{ +public: + IncrementalClearCOMRuleArray() {} + ~IncrementalClearCOMRuleArray() { Clear(); } + + void Clear(); +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_IncrementalClearCOMRuleArray_h) */ diff --git a/layout/style/LayerAnimationInfo.cpp b/layout/style/LayerAnimationInfo.cpp new file mode 100644 index 000000000..8a119512b --- /dev/null +++ b/layout/style/LayerAnimationInfo.cpp @@ -0,0 +1,53 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 "LayerAnimationInfo.h" + +#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags + +namespace mozilla { + +/* static */ const LayerAnimationInfo::Record LayerAnimationInfo::sRecords[] = + { { eCSSProperty_transform, + nsDisplayItem::TYPE_TRANSFORM, + nsChangeHint_UpdateTransformLayer }, + { eCSSProperty_opacity, + nsDisplayItem::TYPE_OPACITY, + nsChangeHint_UpdateOpacityLayer } }; + +#ifdef DEBUG +/* static */ void +LayerAnimationInfo::Initialize() +{ + for (const Record& record : sRecords) { + MOZ_ASSERT(nsCSSProps::PropHasFlags(record.mProperty, + CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR), + "CSS property with entry in LayerAnimation::sRecords does not " + "have the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR flag"); + } + + // Check that every property with the flag for animating on the + // compositor has an entry in LayerAnimationInfo::sRecords. + for (nsCSSPropertyID prop = nsCSSPropertyID(0); + prop < eCSSProperty_COUNT; + prop = nsCSSPropertyID(prop + 1)) { + if (nsCSSProps::PropHasFlags(prop, + CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR)) { + bool found = false; + for (const Record& record : sRecords) { + if (record.mProperty == prop) { + found = true; + break; + } + } + MOZ_ASSERT(found, + "CSS property with the CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR " + "flag does not have an entry in LayerAnimationInfo::sRecords"); + } + } +} +#endif + +} // namespace mozilla diff --git a/layout/style/LayerAnimationInfo.h b/layout/style/LayerAnimationInfo.h new file mode 100644 index 000000000..53c5ae9bf --- /dev/null +++ b/layout/style/LayerAnimationInfo.h @@ -0,0 +1,33 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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_LayerAnimationInfo_h +#define mozilla_LayerAnimationInfo_h + +#include "nsChangeHint.h" +#include "nsCSSPropertyID.h" +#include "nsDisplayList.h" // For nsDisplayItem::Type + +namespace mozilla { + +struct LayerAnimationInfo { +#ifdef DEBUG + static void Initialize(); +#endif + // For CSS properties that may be animated on a separate layer, represents + // a record of the corresponding layer type and change hint. + struct Record { + nsCSSPropertyID mProperty; + nsDisplayItem::Type mLayerType; + nsChangeHint mChangeHint; + }; + + static const size_t kRecords = 2; + static const Record sRecords[kRecords]; +}; + +} // namespace mozilla + +#endif /* !defined(mozilla_LayerAnimationInfo_h) */ diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp new file mode 100644 index 000000000..a1a0fcfd9 --- /dev/null +++ b/layout/style/Loader.cpp @@ -0,0 +1,2694 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: ft=cpp tw=78 sw=2 et ts=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 Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are Copyright (c) + * International Business Machines Corporation, 2000. Modifications + * to Mozilla code or documentation identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. OS/2 VisualAge build. + */ + +/* loading of CSS style sheets using the network APIs */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/LoadInfo.h" +#include "mozilla/MemoryReporting.h" + +#include "mozilla/css/Loader.h" +#include "mozilla/StyleSheetInlines.h" +#include "nsIRunnable.h" +#include "nsIUnicharStreamLoader.h" +#include "nsSyncLoadService.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMDocument.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsIProtocolHandler.h" +#include "nsContentUtils.h" +#include "nsIScriptSecurityManager.h" +#include "nsContentPolicyUtils.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIClassOfService.h" +#include "nsIScriptError.h" +#include "nsMimeTypes.h" +#include "nsIStyleSheetLinkingElement.h" +#include "nsICSSLoaderObserver.h" +#include "nsCSSParser.h" +#include "mozilla/css/ImportRule.h" +#include "nsThreadUtils.h" +#include "nsGkAtoms.h" +#include "nsIThreadInternal.h" +#include "nsINetworkPredictor.h" +#include "mozilla/dom/ShadowRoot.h" +#include "mozilla/dom/URL.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/ConsoleReportCollector.h" + +#ifdef MOZ_XUL +#include "nsXULPrototypeCache.h" +#endif + +#include "nsIMediaList.h" +#include "nsIDOMStyleSheet.h" +#include "nsError.h" + +#include "nsIContentSecurityPolicy.h" +#include "mozilla/dom/SRICheck.h" + +#include "mozilla/dom/EncodingUtils.h" +using mozilla::dom::EncodingUtils; + +using namespace mozilla::dom; + +/** + * OVERALL ARCHITECTURE + * + * The CSS Loader gets requests to load various sorts of style sheets: + * inline style from + + + + +
+ +
+
+ + + + +
+ + diff --git a/layout/style/crashtests/1066089-1.html b/layout/style/crashtests/1066089-1.html new file mode 100644 index 000000000..019a20a7f --- /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 000000000..b76855cc1 --- /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 000000000..f7a2f5961 --- /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 000000000..920ec9c7b --- /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 000000000..bdf63f9c0 --- /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 000000000..e3f8f2aa3 --- /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 000000000..8035f1b21 --- /dev/null +++ b/layout/style/crashtests/1153693-1.html @@ -0,0 +1,22 @@ + + + + + + + + +
    +
    +
    + + + + diff --git a/layout/style/crashtests/1161320-1.html b/layout/style/crashtests/1161320-1.html new file mode 100644 index 000000000..3cb3a7b45 --- /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 000000000..71db694d0 --- /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 000000000..d4eacccdc --- /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 000000000..a3ca0c44d --- /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 000000000..ee5a60ffd --- /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 000000000..4b41ea398 --- /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 000000000..22608557d --- /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 000000000..e2dc9c09d --- /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 000000000..88af39e53 --- /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 000000000..70f9d8579 --- /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 000000000..c4589884f --- /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 000000000..dea15642c --- /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/1227501-1.html b/layout/style/crashtests/1227501-1.html new file mode 100644 index 000000000..03383813d --- /dev/null +++ b/layout/style/crashtests/1227501-1.html @@ -0,0 +1,8 @@ + + + + + + diff --git a/layout/style/crashtests/1230408-1.html b/layout/style/crashtests/1230408-1.html new file mode 100644 index 000000000..0a2588c8a --- /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 000000000..58a82f304 --- /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 000000000..03e356ae1 --- /dev/null +++ b/layout/style/crashtests/1233135-2.html @@ -0,0 +1,11 @@ + + +
    + + + + + + + +
    diff --git a/layout/style/crashtests/1238660-1.html b/layout/style/crashtests/1238660-1.html new file mode 100644 index 000000000..2323d2cfa --- /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 000000000..6f2dda995 --- /dev/null +++ b/layout/style/crashtests/1245260-1.html @@ -0,0 +1,53 @@ + + + + +Bug 1245260 + + + + + + + + diff --git a/layout/style/crashtests/1264396-1.html b/layout/style/crashtests/1264396-1.html new file mode 100644 index 000000000..abba4de3b --- /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 000000000..780bff97a --- /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 000000000..7685ad633 --- /dev/null +++ b/layout/style/crashtests/1265611-1.html @@ -0,0 +1,16 @@ + + + diff --git a/layout/style/crashtests/1270795.html b/layout/style/crashtests/1270795.html new file mode 100644 index 000000000..c4262078e --- /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 000000000..7960d2889 --- /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 000000000..6f9ca3bd0 --- /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 000000000..4c5266826 --- /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 000000000..da3b976d8 --- /dev/null +++ b/layout/style/crashtests/1278463-1.html @@ -0,0 +1,21 @@ + + + + + + +
    + + diff --git a/layout/style/crashtests/1282076-1.html b/layout/style/crashtests/1282076-1.html new file mode 100644 index 000000000..d5d1f0c74 --- /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 000000000..1c6f98639 --- /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 000000000..d9d99a5b0 --- /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 000000000..e592b84ee --- /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 000000000..76589f5a6 --- /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 000000000..4278856d0 --- /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 000000000..8e804643f --- /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 000000000..29186fac1 --- /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 000000000..e0192460f --- /dev/null +++ b/layout/style/crashtests/1315894-1.html @@ -0,0 +1,9 @@ + +
    x
    + diff --git a/layout/style/crashtests/1321357-1.html b/layout/style/crashtests/1321357-1.html new file mode 100644 index 000000000..7b3a3c39c --- /dev/null +++ b/layout/style/crashtests/1321357-1.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/layout/style/crashtests/1356601-1.html b/layout/style/crashtests/1356601-1.html new file mode 100644 index 000000000..4fc28bd58 --- /dev/null +++ b/layout/style/crashtests/1356601-1.html @@ -0,0 +1,18 @@ + + + +
    + Crash +
    + diff --git a/layout/style/crashtests/147777-1.html b/layout/style/crashtests/147777-1.html new file mode 100644 index 000000000..2500fae9f --- /dev/null +++ b/layout/style/crashtests/147777-1.html @@ -0,0 +1,6 @@ + +crashtest for NS_ABORT_IF_FALSE during development of 147777 + +example diff --git a/layout/style/crashtests/187671-1.html b/layout/style/crashtests/187671-1.html new file mode 100644 index 000000000..7395c23b4 --- /dev/null +++ b/layout/style/crashtests/187671-1.html @@ -0,0 +1,12 @@ +Link text + +

    Paragraph text

    + + + + + + diff --git a/layout/style/crashtests/192408-1.html b/layout/style/crashtests/192408-1.html new file mode 100644 index 000000000..bb75e4401 --- /dev/null +++ b/layout/style/crashtests/192408-1.html @@ -0,0 +1,15 @@ + + + bug 192408 + + + + + +
    + +
    + + diff --git a/layout/style/crashtests/285727-1.html b/layout/style/crashtests/285727-1.html new file mode 100644 index 000000000..db1d28d8d --- /dev/null +++ b/layout/style/crashtests/285727-1.html @@ -0,0 +1,13 @@ + + +1 + +
    +
    +1 + +1 + +
    +
    + diff --git a/layout/style/crashtests/286707-1.html b/layout/style/crashtests/286707-1.html new file mode 100644 index 000000000..7485a9644 --- /dev/null +++ b/layout/style/crashtests/286707-1.html @@ -0,0 +1,2 @@ +

    hi1 + diff --git a/layout/style/crashtests/317561-1.html b/layout/style/crashtests/317561-1.html new file mode 100644 index 000000000..01335a17c --- /dev/null +++ b/layout/style/crashtests/317561-1.html @@ -0,0 +1,104 @@ + + + + + + +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/crashtests/330998-1.html b/layout/style/crashtests/330998-1.html new file mode 100644 index 000000000..46522094e --- /dev/null +++ b/layout/style/crashtests/330998-1.html @@ -0,0 +1,30 @@ + + + + + + + + + + +
    + +
    + + + + diff --git a/layout/style/crashtests/363950.html b/layout/style/crashtests/363950.html new file mode 100644 index 000000000..46bfb2f9c --- /dev/null +++ b/layout/style/crashtests/363950.html @@ -0,0 +1,20 @@ + + +Testcase bug 363950 - crash [@ nsComputedDOMStyle::GetMarginWidthCoordFor] + + +This page should not crash Mozilla + + + diff --git a/layout/style/crashtests/368175-1.html b/layout/style/crashtests/368175-1.html new file mode 100644 index 000000000..c7eeddf8c --- /dev/null +++ b/layout/style/crashtests/368175-1.html @@ -0,0 +1,14 @@ + + + + + +
    +
    +

    滾滾長江東逝水,浪花淘盡英雄。是非成敗轉頭空:青山依舊在,幾度夕陽紅。

    +

    + 滾滾長江東逝水,浪花淘盡英雄。是非成敗轉頭空:青山依舊在,幾度夕陽紅。

    +
    +
    + + diff --git a/layout/style/crashtests/368740.html b/layout/style/crashtests/368740.html new file mode 100644 index 000000000..ed315dd44 --- /dev/null +++ b/layout/style/crashtests/368740.html @@ -0,0 +1,25 @@ + + +Testcase bug - Crash [@ nsIFrame::IsThemed] when trying to get the computed style of a button in iframe + + + + +This page should not crash Mozilla
    + + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/379788-1.html b/layout/style/crashtests/379788-1.html new file mode 100644 index 000000000..40e38a7cc --- /dev/null +++ b/layout/style/crashtests/379788-1.html @@ -0,0 +1,9 @@ + + + + +

    Foo

    +

    Bar

    + + + diff --git a/layout/style/crashtests/383979-1.xhtml b/layout/style/crashtests/383979-1.xhtml new file mode 100644 index 000000000..9a3f8e673 --- /dev/null +++ b/layout/style/crashtests/383979-1.xhtml @@ -0,0 +1,31 @@ + + + + + + + + + +
    +
    foo
    + + + diff --git a/layout/style/crashtests/383979-2.html b/layout/style/crashtests/383979-2.html new file mode 100644 index 000000000..06f900e12 --- /dev/null +++ b/layout/style/crashtests/383979-2.html @@ -0,0 +1,36 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/crashtests/386939-1.html b/layout/style/crashtests/386939-1.html new file mode 100644 index 000000000..679d2b27e --- /dev/null +++ b/layout/style/crashtests/386939-1.html @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/layout/style/crashtests/391034-1.xhtml b/layout/style/crashtests/391034-1.xhtml new file mode 100644 index 000000000..315796d28 --- /dev/null +++ b/layout/style/crashtests/391034-1.xhtml @@ -0,0 +1,17 @@ + + + + + + + +

    foo

    + + + diff --git a/layout/style/crashtests/397022-1.html b/layout/style/crashtests/397022-1.html new file mode 100644 index 000000000..ececc3fa5 --- /dev/null +++ b/layout/style/crashtests/397022-1.html @@ -0,0 +1,17 @@ + + + + + + + +
    + + + diff --git a/layout/style/crashtests/399289-1.svg b/layout/style/crashtests/399289-1.svg new file mode 100644 index 000000000..583de2c24 --- /dev/null +++ b/layout/style/crashtests/399289-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/layout/style/crashtests/404470-1.html b/layout/style/crashtests/404470-1.html new file mode 100644 index 000000000..481ba4233 --- /dev/null +++ b/layout/style/crashtests/404470-1.html @@ -0,0 +1,15 @@ + + + + + + + diff --git a/layout/style/crashtests/411603-1.html b/layout/style/crashtests/411603-1.html new file mode 100644 index 000000000..596565fbc --- /dev/null +++ b/layout/style/crashtests/411603-1.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/layout/style/crashtests/412588-1.html b/layout/style/crashtests/412588-1.html new file mode 100644 index 000000000..561aed564 --- /dev/null +++ b/layout/style/crashtests/412588-1.html @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/layout/style/crashtests/413274-1.xhtml b/layout/style/crashtests/413274-1.xhtml new file mode 100644 index 000000000..19d8fab0f --- /dev/null +++ b/layout/style/crashtests/413274-1.xhtml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/layout/style/crashtests/416461-1.xul b/layout/style/crashtests/416461-1.xul new file mode 100644 index 000000000..1986cda91 --- /dev/null +++ b/layout/style/crashtests/416461-1.xul @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/layout/style/crashtests/418007-1.xhtml b/layout/style/crashtests/418007-1.xhtml new file mode 100644 index 000000000..f07a69344 --- /dev/null +++ b/layout/style/crashtests/418007-1.xhtml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/style/crashtests/431705-1.xul b/layout/style/crashtests/431705-1.xul new file mode 100644 index 000000000..8b64d4b3b --- /dev/null +++ b/layout/style/crashtests/431705-1.xul @@ -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 000000000..81bb082e4 --- /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 000000000..af6fa1d66 --- /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 000000000..52eefa530 --- /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 000000000..f22660726 --- /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 000000000..7eac32ed8 --- /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 000000000..d2c75d576 --- /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 000000000..dde700fae --- /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 000000000..05f12bf72 --- /dev/null +++ b/layout/style/crashtests/447783-1.html @@ -0,0 +1,8 @@ + + +Hang with -moz-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 000000000..ef200462b --- /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 000000000..41dc7800f --- /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 000000000..4ff411c6d --- /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 000000000..ed82ca0d4 --- /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 000000000..d78235738 --- /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 000000000..e5918ad6e --- /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 000000000..0bae55aaf --- /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 000000000..f15c934f1 --- /dev/null +++ b/layout/style/crashtests/466845-1.html @@ -0,0 +1,14 @@ + + +Crash [@ nsViewManager::CreateView] with ::first-line position: absolute and -moz-transform + + + + +ع ع ع + + + + diff --git a/layout/style/crashtests/469432-1.xhtml b/layout/style/crashtests/469432-1.xhtml new file mode 100644 index 000000000..9b11a88c4 --- /dev/null +++ b/layout/style/crashtests/469432-1.xhtml @@ -0,0 +1,8 @@ + + + + + diff --git a/layout/style/crashtests/972199-1.html b/layout/style/crashtests/972199-1.html new file mode 100644 index 000000000..a4e0de0d8 --- /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 000000000..a2879d973 --- /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 000000000..86a1d57d7 --- /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 000000000..deefd19b2 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 000000000..b6e3ae5d7 --- /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/crashtests.list b/layout/style/crashtests/crashtests.list new file mode 100644 index 000000000..1ddb01d6b --- /dev/null +++ b/layout/style/crashtests/crashtests.list @@ -0,0 +1,166 @@ +load 105619-1.html +load 147777-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 +load 416461-1.xul +load 418007-1.xhtml +load 431705-1.xul +load 432561-1.html +load 437170-1.html +load 437532-1.html +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 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 621596-1.html +load 622314-1.xhtml +load 637242.xhtml +load 645142.html +== 645951-1.html 645951-1-ref.html +load 652976-1.svg +load 665209-1.html +load 671799-1.html +load 671799-2.html +load 690990-1.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 812824.html +load 822766-1.html +load 822842.html +load 827591-1.html +load 829817.html +load 840898.html +load 842134.html +load 861489-1.html +load 862113.html +load 867487.html +load 873222.html +load 880862.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 +pref(dom.webcomponents.enabled,true) load 1017798-1.html +load 1028514-1.html +load 1066089-1.html +load 1074651-1.html +load 1135534.html +pref(dom.webcomponents.enabled,true) load 1089463-1.html +pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html +pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html +load 1153693-1.html +load 1161320-1.html +pref(dom.animations-api.core.enabled,true) 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 1227501-1.html +load 1230408-1.html +load 1233135-1.html +load 1233135-2.html +load 1238660-1.html +load 1245260-1.html +load 1247865-1.html +load 1264396-1.html +# The following test relies on -webkit-text-fill-color being behind the +# layout.css.prefixes.webkit pref +pref(layout.css.prefixes.webkit,false) load 1265611-1.html +load border-image-visited-link.html +load font-face-truncated-src.html +load large_border_image_width.html +load long-url-list-stack-overflow.html +pref(layout.css.background-clip-text.enabled,true) load 1264949.html +pref(layout.css.background-clip-text.enabled,true) load 1270795.html +pref(layout.css.background-clip-text.enabled,true) load 1275026.html +load 1278463-1.html +pref(dom.animations-api.core.enabled,true) load 1277908-1.html +load 1277908-2.html +load 1282076-1.html +pref(dom.animations-api.core.enabled,true) load 1282076-2.html +pref(dom.animations-api.core.enabled,true) load 1290994-1.html +pref(dom.animations-api.core.enabled,true) load 1290994-2.html +pref(dom.animations-api.core.enabled,true) load 1290994-3.html +load 1290994-4.html +load 1314531.html +load 1315889-1.html +load 1315894-1.html +load 1321357-1.html +load 1356601-1.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 000000000..c3d7dbda5 --- /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 000000000..915d94eb1 --- /dev/null +++ b/layout/style/crashtests/large_border_image_width.html @@ -0,0 +1,9 @@ + + + + + +
    + + + 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 000000000..899e858df --- /dev/null +++ b/layout/style/crashtests/long-url-list-stack-overflow.html @@ -0,0 +1,23 @@ + + + + + + + +
    + + diff --git a/layout/style/designmode.css b/layout/style/designmode.css new file mode 100644 index 000000000..194586ee1 --- /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/generate-stylestructlist.py b/layout/style/generate-stylestructlist.py new file mode 100755 index 000000000..346a7deae --- /dev/null +++ b/layout/style/generate-stylestructlist.py @@ -0,0 +1,165 @@ +#!/usr/bin/env 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/. + +# This script generates nsStyleStructList.h, which contains macro invocations +# that can be used for three things: +# +# 1. To generate code for each inherited style struct. +# 2. To generate code for each reset style struct. +# 3. To generate the dependency of each style struct. + +from __future__ import print_function + +import math + +NORMAL_DEP = ["Variables"] +COLOR_DEP = ["Color"] +LENGTH_DEP = ["Font", "Visibility"] + +# List of style structs and their corresponding Check callback functions, +# if any. +STYLE_STRUCTS = [("INHERITED",) + x for x in [ + # Inherited style structs. + ("Font", "CheckFontCallback", NORMAL_DEP + ["Visibility"]), + ("Color", "CheckColorCallback", NORMAL_DEP), + ("List", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("Text", "CheckTextCallback", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Visibility", "nullptr", NORMAL_DEP), + ("UserInterface", "nullptr", NORMAL_DEP), + ("TableBorder", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("SVG", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Variables", "CheckVariablesCallback",[]), +]] + [("RESET",) + x for x in [ + # Reset style structs. + ("Background", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Position", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("TextReset", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Display", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("Content", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("UIReset", "nullptr", NORMAL_DEP), + ("Table", "nullptr", NORMAL_DEP), + ("Margin", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("Padding", "nullptr", NORMAL_DEP + LENGTH_DEP), + ("Border", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Outline", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("XUL", "nullptr", NORMAL_DEP), + ("SVGReset", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Column", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), + ("Effects", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP), +]] + + +# ---- Generate nsStyleStructList.h ---- + +count = len(STYLE_STRUCTS) + +# Check for problems with style struct dependencies +resolved_items = [] +# This whole loop tries to sort the style structs in topological order +# according to the dependencies. A topological order exists iff there +# are no cyclic dependencies between the style structs. It resolves one +# struct each iteration, and append the resolved one to |resolved_items|. +for i in range(count): + # This inner loop picks one style struct which does not have + # unsolved dependencies. If nothing can be picked, then we + # must have some cyclic dependencies. + for j in range(count): + _, name, _, dependencies = STYLE_STRUCTS[j] + if name in resolved_items: + continue + # Check whether all dependencies of this item have been placed + for dep in dependencies: + if dep not in resolved_items: + break + else: + resolved_items.append(name) + break + else: + import sys + print("ERROR: Cannot resolve style struct dependencies", file=sys.stderr) + print("Resolved items:", " ".join(resolved_items), file=sys.stderr) + unsolved_items = [name for _, name, _, _ in STYLE_STRUCTS + if name not in resolved_items] + print("There exist cyclic dependencies between " + + "the following structs:", " ".join(unsolved_items), file=sys.stderr) + exit(1) + +def printEntry(header, i): + print("STYLE_STRUCT_%s(%s, %s)" % STYLE_STRUCTS[i][:3], file=header) + for dep in STYLE_STRUCTS[i][3]: + print("STYLE_STRUCT_DEP(%s)" % (dep,), file=header) + print("STYLE_STRUCT_END()", file=header) + +HEADER = """/* THIS FILE IS AUTOGENERATED BY generate-stylestructlist.py - DO NOT EDIT */ + +// IWYU pragma: private, include "nsStyleStructFwd.h" + +/* + * list of structs that contain the data provided by nsStyleContext, the + * internal API for computed style data for an element + */ + +/* + * This file is intended to be used by different parts of the code, with + * the STYLE_STRUCT macro (or the STYLE_STRUCT_INHERITED and + * STYLE_STRUCT_RESET pair of macros) defined in different ways. + */ + +#ifndef STYLE_STRUCT_INHERITED +#define STYLE_STRUCT_INHERITED(name, checkdata_cb) \\ + STYLE_STRUCT(name, checkdata_cb) +#define UNDEF_STYLE_STRUCT_INHERITED +#endif + +#ifndef STYLE_STRUCT_RESET +#define STYLE_STRUCT_RESET(name, checkdata_cb) \\ + STYLE_STRUCT(name, checkdata_cb) +#define UNDEF_STYLE_STRUCT_RESET +#endif + +#ifndef STYLE_STRUCT_DEP +#define STYLE_STRUCT_DEP(dep) +#define UNDEF_STYLE_STRUCT_DEP +#endif + +#ifndef STYLE_STRUCT_END +#define STYLE_STRUCT_END() +#define UNDEF_STYLE_STRUCT_END +#endif + +// The inherited structs are listed before the Reset structs. +// nsStyleStructID assumes this is the case, and callers other than +// nsStyleStructFwd.h that want the structs in id-order just define +// STYLE_STRUCT rather than including the file twice. + +""" +FOOTER = """ +#ifdef UNDEF_STYLE_STRUCT_INHERITED +#undef STYLE_STRUCT_INHERITED +#undef UNDEF_STYLE_STRUCT_INHERITED +#endif + +#ifdef UNDEF_STYLE_STRUCT_RESET +#undef STYLE_STRUCT_RESET +#undef UNDEF_STYLE_STRUCT_RESET +#endif + +#ifdef UNDEF_STYLE_STRUCT_DEP +#undef STYLE_STRUCT_DEP +#undef UNDEF_STYLE_STRUCT_DEP +#endif + +#ifdef UNDEF_STYLE_STRUCT_END +#undef STYLE_STRUCT_END +#undef UNDEF_STYLE_STRUCT_END +#endif +""" + +def main(header): + print(HEADER, file=header) + for i in range(count): + printEntry(header, i) + print(FOOTER, file=header) diff --git a/layout/style/jar.mn b/layout/style/jar.mn new file mode 100644 index 000000000..af8c15d4f --- /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/plaintext.css (res/plaintext.css) + res/viewsource.css (res/viewsource.css) + res/counterstyles.css (res/counterstyles.css) + res/noscript.css (res/noscript.css) + res/noframes.css (res/noframes.css) +* res/forms.css (res/forms.css) + res/number-control.css (res/number-control.css) + res/arrow.gif (res/arrow.gif) + res/arrow-left.gif (res/arrow-left.gif) + res/arrow-right.gif (res/arrow-right.gif) + res/arrowd.gif (res/arrowd.gif) + res/arrowd-left.gif (res/arrowd-left.gif) + res/arrowd-right.gif (res/arrowd-right.gif) + 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) + +% resource gre-resources %res/ diff --git a/layout/style/moz.build b/layout/style/moz.build new file mode 100644 index 000000000..3dc2a19af --- /dev/null +++ b/layout/style/moz.build @@ -0,0 +1,264 @@ +# -*- 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') + +DIRS += ['xbl-marquee'] +TEST_DIRS += ['test'] + +XPIDL_SOURCES += [ + 'nsICSSUnprefixingService.idl', +] + +XPIDL_MODULE = 'layout_base' + +EXPORTS += [ + '!nsStyleStructList.h', + 'AnimationCommon.h', + 'CounterStyleManager.h', + 'nsAnimationManager.h', + 'nsComputedDOMStylePropertyList.h', + 'nsCSSAnonBoxes.h', + 'nsCSSAnonBoxList.h', + 'nsCSSCounterDescList.h', + 'nsCSSFontDescList.h', + 'nsCSSKeywordList.h', + 'nsCSSKeywords.h', + 'nsCSSParser.h', + 'nsCSSPropAliasList.h', + 'nsCSSPropertyID.h', + 'nsCSSPropertyIDSet.h', + 'nsCSSPropList.h', + 'nsCSSPropLogicalGroupList.h', + 'nsCSSProps.h', + 'nsCSSPseudoClasses.h', + 'nsCSSPseudoClassList.h', + 'nsCSSPseudoElementList.h', + 'nsCSSPseudoElements.h', + 'nsCSSRuleProcessor.h', + 'nsCSSScanner.h', + 'nsCSSValue.h', + 'nsDOMCSSAttrDeclaration.h', + 'nsDOMCSSDeclaration.h', + 'nsDOMCSSRGBColor.h', + 'nsICSSDeclaration.h', + 'nsICSSLoaderObserver.h', + 'nsICSSPseudoComparator.h', + 'nsICSSStyleRuleDOMWrapper.h', + 'nsIStyleRule.h', + 'nsIStyleRuleProcessor.h', + 'nsLayoutStylesheetCache.h', + 'nsRuleData.h', + 'nsRuleNode.h', + 'nsRuleProcessorData.h', + 'nsRuleWalker.h', + 'nsStyleConsts.h', + 'nsStyleContext.h', + 'nsStyleCoord.h', + 'nsStyleSet.h', + 'nsStyleStruct.h', + 'nsStyleStructFwd.h', + 'nsStyleStructInlines.h', + 'nsStyleTransformMatrix.h', + 'nsStyleUtil.h', +] + +EXPORTS.mozilla += [ + 'AnimationCollection.h', + 'CSSEnabledState.h', + 'CSSStyleSheet.h', + 'CSSVariableDeclarations.h', + 'CSSVariableResolver.h', + 'CSSVariableValues.h', + 'DeclarationBlock.h', + 'DeclarationBlockInlines.h', + 'HandleRefPtr.h', + 'IncrementalClearCOMRuleArray.h', + 'LayerAnimationInfo.h', + 'RuleNodeCacheConditions.h', + 'RuleProcessorCache.h', + 'ServoBindingList.h', + 'ServoBindings.h', + 'ServoBindingTypes.h', + 'ServoDeclarationBlock.h', + 'ServoElementSnapshot.h', + 'ServoStyleSet.h', + 'ServoStyleSheet.h', + 'ServoTypes.h', + 'ServoUtils.h', + 'SheetType.h', + 'StyleAnimationValue.h', + 'StyleBackendType.h', + 'StyleComplexColor.h', + 'StyleContextSource.h', + 'StyleSetHandle.h', + 'StyleSetHandleInlines.h', + 'StyleSheet.h', + 'StyleSheetInfo.h', + 'StyleSheetInlines.h', + 'StyleStructContext.h', +] + +EXPORTS.mozilla.dom += [ + 'CSS.h', + 'CSSLexer.h', + 'CSSRuleList.h', + 'CSSValue.h', + 'FontFace.h', + 'FontFaceSet.h', + 'FontFaceSetIterator.h', + 'MediaQueryList.h', +] + +EXPORTS.mozilla.css += [ + 'Declaration.h', + 'ErrorReporter.h', + 'GroupRule.h', + 'ImageLoader.h', + 'ImportRule.h', + 'Loader.h', + 'NameSpaceRule.h', + 'Rule.h', + 'SheetParsingMode.h', + 'StyleRule.h', +] + +UNIFIED_SOURCES += [ + 'AnimationCollection.cpp', + 'AnimationCommon.cpp', + 'CounterStyleManager.cpp', + 'CSS.cpp', + 'CSSLexer.cpp', + 'CSSRuleList.cpp', + 'CSSStyleSheet.cpp', + 'CSSVariableDeclarations.cpp', + 'CSSVariableResolver.cpp', + 'CSSVariableValues.cpp', + 'Declaration.cpp', + 'ErrorReporter.cpp', + 'FontFace.cpp', + 'FontFaceSet.cpp', + 'FontFaceSetIterator.cpp', + 'ImageLoader.cpp', + 'IncrementalClearCOMRuleArray.cpp', + 'LayerAnimationInfo.cpp', + 'Loader.cpp', + 'MediaQueryList.cpp', + 'nsAnimationManager.cpp', + 'nsComputedDOMStyle.cpp', + 'nsCSSAnonBoxes.cpp', + 'nsCSSDataBlock.cpp', + 'nsCSSKeywords.cpp', + 'nsCSSParser.cpp', + 'nsCSSProps.cpp', + 'nsCSSPseudoClasses.cpp', + 'nsCSSPseudoElements.cpp', + 'nsCSSRules.cpp', + 'nsCSSScanner.cpp', + 'nsCSSValue.cpp', + 'nsDOMCSSAttrDeclaration.cpp', + 'nsDOMCSSDeclaration.cpp', + 'nsDOMCSSRect.cpp', + 'nsDOMCSSRGBColor.cpp', + 'nsDOMCSSValueList.cpp', + 'nsFontFaceLoader.cpp', + 'nsFontFaceUtils.cpp', + 'nsHTMLCSSStyleSheet.cpp', + 'nsHTMLStyleSheet.cpp', + 'nsMediaFeatures.cpp', + 'nsNthIndexCache.cpp', + 'nsROCSSPrimitiveValue.cpp', + 'nsRuleData.cpp', + 'nsRuleNode.cpp', + 'nsStyleContext.cpp', + 'nsStyleCoord.cpp', + 'nsStyleSet.cpp', + 'nsStyleStruct.cpp', + 'nsStyleTransformMatrix.cpp', + 'nsStyleUtil.cpp', + 'nsTransitionManager.cpp', + 'RuleNodeCacheConditions.cpp', + 'RuleProcessorCache.cpp', + 'ServoBindings.cpp', + 'ServoDeclarationBlock.cpp', + 'ServoElementSnapshot.cpp', + 'ServoStyleSet.cpp', + 'ServoStyleSheet.cpp', + 'StyleAnimationValue.cpp', + 'StyleRule.cpp', + 'StyleSheet.cpp', + 'SVGAttrAnimationRuleProcessor.cpp', +] + +# nsCSSRuleProcessor.cpp needs to be built separately because it uses plarena.h. +# nsLayoutStylesheetCache.cpp needs to be built separately because it uses +# nsExceptionHandler.h, which includes windows.h. +SOURCES += [ + 'nsCSSRuleProcessor.cpp', + 'nsLayoutStylesheetCache.cpp', +] + +EXTRA_COMPONENTS += [ + 'CSSUnprefixingService.js', + 'CSSUnprefixingService.manifest', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '../base', + '../generic', + '../svg', + '../xul', + '/dom/base', + '/dom/html', + '/dom/xbl', + '/dom/xul', + '/image', +] + +JAR_MANIFESTS += ['jar.mn'] + +RESOURCE_FILES += [ + 'contenteditable.css', + 'designmode.css', + 'ImageDocument.css', + 'TopLevelImageDocument.css', + 'TopLevelVideoDocument.css', +] + +GENERATED_FILES += [ + 'nsStyleStructList.h', +] + +style_struct_list = GENERATED_FILES['nsStyleStructList.h'] +style_struct_list.script = 'generate-stylestructlist.py' + +if CONFIG['COMPILE_ENVIRONMENT']: + GENERATED_FILES += [ + 'nsCSSPropsGenerated.inc', + ] + css_props = GENERATED_FILES['nsCSSPropsGenerated.inc'] + css_props.script = 'GenerateCSSPropsGenerated.py:generate' + css_props.inputs = [ + 'nsCSSPropsGenerated.inc.in', + 'PythonCSSProps.h', + ] diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp new file mode 100644 index 000000000..ed2b5afc7 --- /dev/null +++ b/layout/style/nsAnimationManager.cpp @@ -0,0 +1,1082 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 "nsTransitionManager.h" +#include "mozilla/dom/CSSAnimationBinding.h" + +#include "mozilla/EffectCompositor.h" +#include "mozilla/EffectSet.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/StyleAnimationValue.h" +#include "mozilla/dom/DocumentTimeline.h" +#include "mozilla/dom/KeyframeEffectReadOnly.h" + +#include "nsPresContext.h" +#include "nsStyleSet.h" +#include "nsStyleChangeList.h" +#include "nsCSSRules.h" +#include "mozilla/RestyleManager.h" +#include "nsLayoutUtils.h" +#include "nsIFrame.h" +#include "nsIDocument.h" +#include "nsDOMMutationObserver.h" +#include // std::stable_sort +#include + +using namespace mozilla; +using namespace mozilla::css; +using mozilla::dom::Animation; +using mozilla::dom::AnimationPlayState; +using mozilla::dom::KeyframeEffectReadOnly; +using mozilla::dom::CSSAnimation; + +namespace { + +// Pair of an event message and elapsed time used when determining the set of +// events to queue. +typedef Pair EventPair; + +} // anonymous namespace + +////////////////////////// CSSAnimation //////////////////////////// + +JSObject* +CSSAnimation::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return dom::CSSAnimationBinding::Wrap(aCx, this, aGivenProto); +} + +mozilla::dom::Promise* +CSSAnimation::GetReady(ErrorResult& aRv) +{ + FlushStyle(); + return Animation::GetReady(aRv); +} + +void +CSSAnimation::Play(ErrorResult &aRv, LimitBehavior aLimitBehavior) +{ + mPauseShouldStick = false; + Animation::Play(aRv, aLimitBehavior); +} + +void +CSSAnimation::Pause(ErrorResult& aRv) +{ + mPauseShouldStick = true; + Animation::Pause(aRv); +} + +AnimationPlayState +CSSAnimation::PlayStateFromJS() const +{ + // Flush style to ensure that any properties controlling animation state + // (e.g. animation-play-state) are fully updated. + FlushStyle(); + return Animation::PlayStateFromJS(); +} + +void +CSSAnimation::PlayFromJS(ErrorResult& aRv) +{ + // Note that flushing style below might trigger calls to + // PlayFromStyle()/PauseFromStyle() on this object. + FlushStyle(); + Animation::PlayFromJS(aRv); +} + +void +CSSAnimation::PlayFromStyle() +{ + mIsStylePaused = false; + if (!mPauseShouldStick) { + ErrorResult rv; + PlayNoUpdate(rv, Animation::LimitBehavior::Continue); + // play() should not throw when LimitBehavior is Continue + MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing animation"); + } +} + +void +CSSAnimation::PauseFromStyle() +{ + // Check if the pause state is being overridden + if (mIsStylePaused) { + return; + } + + mIsStylePaused = true; + ErrorResult rv; + PauseNoUpdate(rv); + // pause() should only throw when *all* of the following conditions are true: + // - we are in the idle state, and + // - we have a negative playback rate, and + // - we have an infinitely repeating animation + // The first two conditions will never happen under regular style processing + // but could happen if an author made modifications to the Animation object + // and then updated animation-play-state. It's an unusual case and there's + // no obvious way to pass on the exception information so we just silently + // fail for now. + if (rv.Failed()) { + NS_WARNING("Unexpected exception pausing animation - silently failing"); + } +} + +void +CSSAnimation::Tick() +{ + Animation::Tick(); + QueueEvents(); +} + +bool +CSSAnimation::HasLowerCompositeOrderThan(const CSSAnimation& aOther) const +{ + MOZ_ASSERT(IsTiedToMarkup() && aOther.IsTiedToMarkup(), + "Should only be called for CSS animations that are sorted " + "as CSS animations (i.e. tied to CSS markup)"); + + // 0. Object-equality case + if (&aOther == this) { + return false; + } + + // 1. Sort by document order + if (!mOwningElement.Equals(aOther.mOwningElement)) { + return mOwningElement.LessThan(aOther.mOwningElement); + } + + // 2. (Same element and pseudo): Sort by position in animation-name + return mAnimationIndex < aOther.mAnimationIndex; +} + +void +CSSAnimation::QueueEvents() +{ + if (!mEffect) { + return; + } + + // If the animation is pending, we ignore animation events until we finish + // pending. + if (mPendingState != PendingState::NotPending) { + return; + } + + // CSS animations dispatch events at their owning element. This allows + // script to repurpose a CSS animation to target a different element, + // to use a group effect (which has no obvious "target element"), or + // to remove the animation effect altogether whilst still getting + // animation events. + // + // It does mean, however, that for a CSS animation that has no owning + // element (e.g. it was created using the CSSAnimation constructor or + // disassociated from CSS) no events are fired. If it becomes desirable + // for these animations to still fire events we should spec the concept + // of the "original owning element" or "event target" and allow script + // to set it when creating a CSSAnimation object. + if (!mOwningElement.IsSet()) { + return; + } + + dom::Element* owningElement; + CSSPseudoElementType owningPseudoType; + mOwningElement.GetElement(owningElement, owningPseudoType); + MOZ_ASSERT(owningElement, "Owning element should be set"); + + // Get the nsAnimationManager so we can queue events on it + nsPresContext* presContext = mOwningElement.GetRenderedPresContext(); + if (!presContext) { + return; + } + nsAnimationManager* manager = presContext->AnimationManager(); + + ComputedTiming computedTiming = mEffect->GetComputedTiming(); + + if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Null) { + return; // do nothing + } + + // Note that script can change the start time, so we have to handle moving + // backwards through the animation as well as forwards. An 'animationstart' + // is dispatched if we enter the active phase (regardless if that is from + // before or after the animation's active phase). An 'animationend' is + // dispatched if we leave the active phase (regardless if that is to before + // or after the animation's active phase). + + bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE && + mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER; + bool isActive = + computedTiming.mPhase == ComputedTiming::AnimationPhase::Active; + bool isSameIteration = + computedTiming.mCurrentIteration == mPreviousPhaseOrIteration; + bool skippedActivePhase = + (mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE && + computedTiming.mPhase == ComputedTiming::AnimationPhase::After) || + (mPreviousPhaseOrIteration == PREVIOUS_PHASE_AFTER && + computedTiming.mPhase == ComputedTiming::AnimationPhase::Before); + bool skippedFirstIteration = + isActive && + mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE && + computedTiming.mCurrentIteration > 0; + + MOZ_ASSERT(!skippedActivePhase || (!isActive && !wasActive), + "skippedActivePhase only makes sense if we were & are inactive"); + + if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Before) { + mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE; + } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Active) { + mPreviousPhaseOrIteration = computedTiming.mCurrentIteration; + } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::After) { + mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER; + } + + AutoTArray events; + StickyTimeDuration initialAdvance = StickyTimeDuration(InitialAdvance()); + StickyTimeDuration iterationStart = computedTiming.mDuration * + computedTiming.mCurrentIteration; + const StickyTimeDuration& activeDuration = computedTiming.mActiveDuration; + + if (skippedFirstIteration) { + // Notify animationstart and animationiteration in same tick. + events.AppendElement(EventPair(eAnimationStart, initialAdvance)); + events.AppendElement(EventPair(eAnimationIteration, + std::max(iterationStart, initialAdvance))); + } else if (!wasActive && isActive) { + events.AppendElement(EventPair(eAnimationStart, initialAdvance)); + } else if (wasActive && !isActive) { + events.AppendElement(EventPair(eAnimationEnd, activeDuration)); + } else if (wasActive && isActive && !isSameIteration) { + events.AppendElement(EventPair(eAnimationIteration, iterationStart)); + } else if (skippedActivePhase) { + events.AppendElement(EventPair(eAnimationStart, + std::min(initialAdvance, activeDuration))); + events.AppendElement(EventPair(eAnimationEnd, activeDuration)); + } else { + return; // No events need to be sent + } + + for (const EventPair& pair : events){ + manager->QueueEvent( + AnimationEventInfo(owningElement, owningPseudoType, + pair.first(), mAnimationName, + pair.second(), + ElapsedTimeToTimeStamp(pair.second()), + this)); + } +} + +void +CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) +{ + if (mNeedsNewAnimationIndexWhenRun && + PlayState() != AnimationPlayState::Idle) { + mAnimationIndex = sNextAnimationIndex++; + mNeedsNewAnimationIndexWhenRun = false; + } + + Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag); +} + +////////////////////////// nsAnimationManager //////////////////////////// + +NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsAnimationManager, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsAnimationManager, Release) + +// 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 nsAString& 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; +} + +static void +UpdateOldAnimationPropertiesWithNew( + CSSAnimation& aOld, + TimingParams& aNewTiming, + nsTArray& aNewKeyframes, + bool aNewIsStylePaused, + nsStyleContext* aStyleContext) +{ + 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()) { + AnimationEffectReadOnly* oldEffect = aOld.GetEffect(); + animationChanged = oldEffect->SpecifiedTiming() != aNewTiming; + oldEffect->SetSpecifiedTiming(aNewTiming); + + KeyframeEffectReadOnly* oldKeyframeEffect = oldEffect->AsKeyframeEffect(); + if (oldKeyframeEffect) { + oldKeyframeEffect->SetKeyframes(Move(aNewKeyframes), aStyleContext); + } + } + + // 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) { + // CSSAnimation takes care of override behavior so that, + // for example, if the author has called pause(), that will + // override the animation-play-state. + // (We should check aNew->IsStylePaused() but that requires + // downcasting to CSSAnimation and we happen to know that + // aNew will only ever be paused by calling PauseFromStyle + // making IsPausedOrPausing synonymous in this case.) + if (!aOld.IsStylePaused() && aNewIsStylePaused) { + aOld.PauseFromStyle(); + animationChanged = true; + } else if (aOld.IsStylePaused() && !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()) { + nsNodeUtils::AnimationChanged(&aOld); + } +} + +void +nsAnimationManager::UpdateAnimations(nsStyleContext* aStyleContext, + mozilla::dom::Element* aElement) +{ + 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"); + + // 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. + + const nsStyleDisplay* disp = aStyleContext->StyleDisplay(); + CSSAnimationCollection* collection = + CSSAnimationCollection::GetAnimationCollection(aElement, + aStyleContext-> + GetPseudoType()); + if (!collection && + disp->mAnimationNameCount == 1 && + disp->mAnimations[0].GetName().IsEmpty()) { + return; + } + + nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); + + // Build the updated animations list, extracting matching animations from + // the existing collection as we go. + OwningCSSAnimationPtrArray newAnimations; + if (!aStyleContext->IsInDisplayNoneSubtree()) { + BuildAnimations(aStyleContext, aElement, collection, newAnimations); + } + + if (newAnimations.IsEmpty()) { + if (collection) { + collection->Destroy(); + } + return; + } + + if (!collection) { + bool createdCollection = false; + collection = + CSSAnimationCollection::GetOrCreateAnimationCollection( + aElement, aStyleContext->GetPseudoType(), &createdCollection); + if (!collection) { + MOZ_ASSERT(!createdCollection, "outparam should agree with return value"); + NS_WARNING("allocating collection failed"); + return; + } + + if (createdCollection) { + AddElementCollection(collection); + } + } + collection->mAnimations.SwapElements(newAnimations); + + // Cancel removed animations + for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) { + newAnimations[newAnimIdx]->CancelFromStyle(); + } + + // We don't actually dispatch the pending events now. We'll either + // dispatch them the next time we get a refresh driver notification + // or the next time somebody calls + // nsPresShell::FlushPendingNotifications. + if (mEventDispatcher.HasQueuedEvents()) { + mPresContext->Document()->SetNeedStyleFlush(); + } +} + +void +nsAnimationManager::StopAnimationsForElement( + mozilla::dom::Element* aElement, + mozilla::CSSPseudoElementType aPseudoType) +{ + MOZ_ASSERT(aElement); + CSSAnimationCollection* collection = + CSSAnimationCollection::GetAnimationCollection(aElement, aPseudoType); + if (!collection) { + return; + } + + nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); + collection->Destroy(); +} + +class ResolvedStyleCache { +public: + ResolvedStyleCache() : mCache() {} + nsStyleContext* Get(nsPresContext *aPresContext, + nsStyleContext *aParentStyleContext, + Declaration* aKeyframeDeclaration); + +private: + nsRefPtrHashtable, nsStyleContext> mCache; +}; + +nsStyleContext* +ResolvedStyleCache::Get(nsPresContext *aPresContext, + nsStyleContext *aParentStyleContext, + Declaration* aKeyframeDeclaration) +{ + // FIXME (spec): The css3-animations spec isn't very clear about how + // properties are resolved when they have values that depend on other + // properties (e.g., values in 'em'). I presume that they're resolved + // relative to the other styles of the element. The question is + // whether they are resolved relative to other animations: I assume + // that they're not, since that would prevent us from caching a lot of + // data that we'd really like to cache (in particular, the + // StyleAnimationValue values in AnimationPropertySegment). + nsStyleContext *result = mCache.GetWeak(aKeyframeDeclaration); + if (!result) { + aKeyframeDeclaration->SetImmutable(); + // The spec says that !important declarations should just be ignored + MOZ_ASSERT(!aKeyframeDeclaration->HasImportantData(), + "Keyframe rule has !important data"); + + nsCOMArray rules; + rules.AppendObject(aKeyframeDeclaration); + MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(), + "ServoStyleSet should not use nsAnimationManager for " + "animations"); + RefPtr resultStrong = aPresContext->StyleSet()->AsGecko()-> + ResolveStyleByAddingRules(aParentStyleContext, rules); + mCache.Put(aKeyframeDeclaration, resultStrong); + result = resultStrong; + } + return result; +} + +class MOZ_STACK_CLASS CSSAnimationBuilder final { +public: + CSSAnimationBuilder(nsStyleContext* aStyleContext, + dom::Element* aTarget, + nsAnimationManager::CSSAnimationCollection* aCollection) + : mStyleContext(aStyleContext) + , mTarget(aTarget) + , mCollection(aCollection) + { + MOZ_ASSERT(aStyleContext); + MOZ_ASSERT(aTarget); + mTimeline = mTarget->OwnerDoc()->Timeline(); + } + + // Returns a new animation set up with given StyleAnimation and + // keyframe rules. + // Or returns an existing animation matching StyleAnimation's name updated + // with the new StyleAnimation and keyframe rules. + already_AddRefed + Build(nsPresContext* aPresContext, + const StyleAnimation& aSrc, + const nsCSSKeyframesRule* aRule); + +private: + nsTArray BuildAnimationFrames(nsPresContext* aPresContext, + const StyleAnimation& aSrc, + const nsCSSKeyframesRule* aRule); + Maybe GetKeyframeTimingFunction( + nsPresContext* aPresContext, + nsCSSKeyframeRule* aKeyframeRule, + const Maybe& aInheritedTimingFunction); + nsTArray GetKeyframePropertyValues( + nsPresContext* aPresContext, + nsCSSKeyframeRule* aKeyframeRule, + nsCSSPropertyIDSet& aAnimatedProperties); + void FillInMissingKeyframeValues( + nsPresContext* aPresContext, + nsCSSPropertyIDSet aAnimatedProperties, + nsCSSPropertyIDSet aPropertiesSetAtStart, + nsCSSPropertyIDSet aPropertiesSetAtEnd, + const Maybe& aInheritedTimingFunction, + nsTArray& aKeyframes); + void AppendProperty(nsPresContext* aPresContext, + nsCSSPropertyID aProperty, + nsTArray& aPropertyValues); + nsCSSValue GetComputedValue(nsPresContext* aPresContext, + nsCSSPropertyID aProperty); + + static TimingParams TimingParamsFrom( + const StyleAnimation& aStyleAnimation) + { + TimingParams timing; + + timing.mDuration.emplace(StickyTimeDuration::FromMilliseconds( + aStyleAnimation.GetDuration())); + timing.mDelay = TimeDuration::FromMilliseconds(aStyleAnimation.GetDelay()); + timing.mIterations = aStyleAnimation.GetIterationCount(); + MOZ_ASSERT(timing.mIterations >= 0.0 && !IsNaN(timing.mIterations), + "mIterations should be nonnegative & finite, as ensured by " + "CSSParser"); + timing.mDirection = aStyleAnimation.GetDirection(); + timing.mFill = aStyleAnimation.GetFillMode(); + + return timing; + } + + RefPtr mStyleContext; + RefPtr mTarget; + RefPtr mTimeline; + + ResolvedStyleCache mResolvedStyles; + RefPtr mStyleWithoutAnimation; + // Existing collection, nullptr if the target element has no animations. + nsAnimationManager::CSSAnimationCollection* mCollection; +}; + +static Maybe +ConvertTimingFunction(const nsTimingFunction& aTimingFunction); + +already_AddRefed +CSSAnimationBuilder::Build(nsPresContext* aPresContext, + const StyleAnimation& aSrc, + const nsCSSKeyframesRule* aRule) +{ + MOZ_ASSERT(aPresContext); + MOZ_ASSERT(aRule); + + TimingParams timing = TimingParamsFrom(aSrc); + + nsTArray keyframes = + BuildAnimationFrames(aPresContext, aSrc, aRule); + + bool isStylePaused = + aSrc.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED; + + // Find the matching animation with animation name in the old list + // of animations and remove the matched animation from the list. + RefPtr oldAnim = + PopExistingAnimation(aSrc.GetName(), mCollection); + + 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, + timing, + keyframes, + isStylePaused, + mStyleContext); + return oldAnim.forget(); + } + + // mTarget is non-null here, so we emplace it directly. + Maybe target; + target.emplace(mTarget, mStyleContext->GetPseudoType()); + KeyframeEffectParams effectOptions; + RefPtr effect = + new KeyframeEffectReadOnly(aPresContext->Document(), target, timing, + effectOptions); + + effect->SetKeyframes(Move(keyframes), mStyleContext); + + RefPtr animation = + new CSSAnimation(aPresContext->Document()->GetScopeObject(), + aSrc.GetName()); + animation->SetOwningElement( + OwningElementRef(*mTarget, mStyleContext->GetPseudoType())); + + animation->SetTimelineNoUpdate(mTimeline); + animation->SetEffectNoUpdate(effect); + + if (isStylePaused) { + animation->PauseFromStyle(); + } else { + animation->PlayFromStyle(); + } + + return animation.forget(); +} + +nsTArray +CSSAnimationBuilder::BuildAnimationFrames(nsPresContext* aPresContext, + const StyleAnimation& aSrc, + const nsCSSKeyframesRule* aRule) +{ + // Ideally we'd like to build up a set of Keyframe objects that more-or-less + // reflect the keyframes as-specified in the @keyframes rule(s) so that + // authors get something intuitive when they call anim.effect.getKeyframes(). + // + // That, however, proves to be difficult because the way CSS declarations are + // processed differs from how we are able to represent keyframes as + // JavaScript objects in the Web Animations API. + // + // For example, + // + // { margin: 10px; margin-left: 20px } + // + // could be represented as: + // + // { margin: '10px', marginLeft: '20px' } + // + // BUT: + // + // { margin-left: 20px; margin: 10px } + // + // would be represented as: + // + // { margin: '10px' } + // + // Likewise, + // + // { margin-left: 20px; margin-left: 30px } + // + // would be represented as: + // + // { marginLeft: '30px' } + // + // As such, the mapping between source @keyframes and the Keyframe objects + // becomes obscured. The deviation is even more significant when we consider + // cascading between @keyframes rules and variable references in shorthand + // properties. + // + // We could, perhaps, produce a mapping that makes sense most of the time + // but it would be complex and need to be specified and implemented + // interoperably. Instead, for now, for CSS Animations (and CSS Transitions, + // for that matter) we resolve values on @keyframes down to computed values + // (thereby expanding shorthands and variable references) and then pick up the + // last value for each longhand property at each offset. + + // FIXME: There is a pending spec change to make multiple @keyframes + // rules with the same name cascade but we don't support that yet. + + Maybe inheritedTimingFunction = + ConvertTimingFunction(aSrc.GetTimingFunction()); + + // First, make up Keyframe objects for each rule + nsTArray keyframes; + nsCSSPropertyIDSet animatedProperties; + + for (auto ruleIdx = 0, ruleEnd = aRule->StyleRuleCount(); + ruleIdx != ruleEnd; ++ruleIdx) { + css::Rule* cssRule = aRule->GetStyleRuleAt(ruleIdx); + MOZ_ASSERT(cssRule, "must have rule"); + MOZ_ASSERT(cssRule->GetType() == css::Rule::KEYFRAME_RULE, + "must be keyframe rule"); + nsCSSKeyframeRule* keyframeRule = static_cast(cssRule); + + const nsTArray& keys = keyframeRule->GetKeys(); + for (float key : keys) { + if (key < 0.0f || key > 1.0f) { + continue; + } + + Keyframe keyframe; + keyframe.mOffset.emplace(key); + keyframe.mTimingFunction = + GetKeyframeTimingFunction(aPresContext, keyframeRule, + inheritedTimingFunction); + keyframe.mPropertyValues = + GetKeyframePropertyValues(aPresContext, keyframeRule, + animatedProperties); + + keyframes.AppendElement(Move(keyframe)); + } + } + + // Next, stable sort by offset + std::stable_sort(keyframes.begin(), keyframes.end(), + [](const Keyframe& a, const Keyframe& b) + { + return a.mOffset < b.mOffset; + }); + + // Then walk backwards through the keyframes and drop overridden properties. + nsCSSPropertyIDSet propertiesSetAtCurrentOffset; + nsCSSPropertyIDSet propertiesSetAtStart; + nsCSSPropertyIDSet propertiesSetAtEnd; + double currentOffset = -1.0; + for (size_t keyframeIdx = keyframes.Length(); + keyframeIdx > 0; + --keyframeIdx) { + Keyframe& keyframe = keyframes[keyframeIdx - 1]; + MOZ_ASSERT(keyframe.mOffset, "Should have filled in the offset"); + + if (keyframe.mOffset.value() != currentOffset) { + propertiesSetAtCurrentOffset.Empty(); + currentOffset = keyframe.mOffset.value(); + } + + // Get the set of properties from this keyframe that have not + // already been set at this offset. + nsTArray uniquePropertyValues; + uniquePropertyValues.SetCapacity(keyframe.mPropertyValues.Length()); + for (const PropertyValuePair& pair : keyframe.mPropertyValues) { + if (!propertiesSetAtCurrentOffset.HasProperty(pair.mProperty)) { + uniquePropertyValues.AppendElement(pair); + propertiesSetAtCurrentOffset.AddProperty(pair.mProperty); + + if (currentOffset == 0.0) { + propertiesSetAtStart.AddProperty(pair.mProperty); + } else if (currentOffset == 1.0) { + propertiesSetAtEnd.AddProperty(pair.mProperty); + } + } + } + + // If we have a keyframe at the same offset with the same timing + // function we should merge our (unique) values into it. + // Otherwise, we should update the existing keyframe with only the + // unique properties. + // + // Bug 1293490: We should also match composite modes here. + Keyframe* existingKeyframe = nullptr; + // Don't bother searching for an existing keyframe if we don't + // have anything to contribute to it. + if (!uniquePropertyValues.IsEmpty()) { + for (size_t i = keyframeIdx; i < keyframes.Length(); i++) { + Keyframe& kf = keyframes[i]; + if (kf.mOffset.value() != currentOffset) { + break; + } + if (kf.mTimingFunction == keyframe.mTimingFunction) { + existingKeyframe = &kf; + break; + } + } + } + + if (existingKeyframe) { + existingKeyframe-> + mPropertyValues.AppendElements(Move(uniquePropertyValues)); + keyframe.mPropertyValues.Clear(); + } else { + keyframe.mPropertyValues.SwapElements(uniquePropertyValues); + } + + // Check for a now-empty keyframe + if (keyframe.mPropertyValues.IsEmpty()) { + keyframes.RemoveElementAt(keyframeIdx - 1); + // existingKeyframe might dangle now + } + } + + // Finally, we need to look for any animated properties that have an + // implicit 'to' or 'from' value and fill in the appropriate keyframe + // with the current computed style. + FillInMissingKeyframeValues(aPresContext, animatedProperties, + propertiesSetAtStart, propertiesSetAtEnd, + inheritedTimingFunction, keyframes); + + return keyframes; +} + +Maybe +CSSAnimationBuilder::GetKeyframeTimingFunction( + nsPresContext* aPresContext, + nsCSSKeyframeRule* aKeyframeRule, + const Maybe& aInheritedTimingFunction) +{ + Maybe result; + + if (aKeyframeRule->Declaration() && + aKeyframeRule->Declaration()->HasProperty( + eCSSProperty_animation_timing_function)) { + RefPtr keyframeRuleContext = + mResolvedStyles.Get(aPresContext, mStyleContext, + aKeyframeRule->Declaration()); + const nsTimingFunction& tf = keyframeRuleContext->StyleDisplay()-> + mAnimations[0].GetTimingFunction(); + result = ConvertTimingFunction(tf); + } else { + result = aInheritedTimingFunction; + } + + return result; +} + +static Maybe +ConvertTimingFunction(const nsTimingFunction& aTimingFunction) +{ + Maybe result; + + if (aTimingFunction.mType != nsTimingFunction::Type::Linear) { + result.emplace(); + result->Init(aTimingFunction); + } + + return result; +} + +nsTArray +CSSAnimationBuilder::GetKeyframePropertyValues( + nsPresContext* aPresContext, + nsCSSKeyframeRule* aKeyframeRule, + nsCSSPropertyIDSet& aAnimatedProperties) +{ + nsTArray result; + RefPtr styleContext = + mResolvedStyles.Get(aPresContext, mStyleContext, + aKeyframeRule->Declaration()); + + for (nsCSSPropertyID prop = nsCSSPropertyID(0); + prop < eCSSProperty_COUNT_no_shorthands; + prop = nsCSSPropertyID(prop + 1)) { + if (nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None || + !aKeyframeRule->Declaration()->HasNonImportantValueFor(prop)) { + continue; + } + + PropertyValuePair pair; + pair.mProperty = prop; + + StyleAnimationValue computedValue; + if (!StyleAnimationValue::ExtractComputedValue(prop, styleContext, + computedValue)) { + continue; + } + DebugOnly uncomputeResult = + StyleAnimationValue::UncomputeValue(prop, Move(computedValue), + pair.mValue); + MOZ_ASSERT(uncomputeResult, + "Unable to get specified value from computed value"); + MOZ_ASSERT(pair.mValue.GetUnit() != eCSSUnit_Null, + "Not expecting to read invalid properties"); + + result.AppendElement(Move(pair)); + aAnimatedProperties.AddProperty(prop); + } + + return result; +} + +// 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 Maybe& aTimingFunctionToMatch, + size_t& aIndex) +{ + aIndex = 0; + for (Keyframe& keyframe : aIter) { + if (keyframe.mOffset.value() != aOffset) { + break; + } + if (keyframe.mTimingFunction == aTimingFunctionToMatch) { + return true; + } + ++aIndex; + } + return false; +} + +void +CSSAnimationBuilder::FillInMissingKeyframeValues( + nsPresContext* aPresContext, + nsCSSPropertyIDSet aAnimatedProperties, + nsCSSPropertyIDSet aPropertiesSetAtStart, + nsCSSPropertyIDSet aPropertiesSetAtEnd, + const Maybe& aInheritedTimingFunction, + nsTArray& aKeyframes) +{ + static const size_t kNotSet = static_cast(-1); + + // Find/create the keyframe to add start values to + size_t startKeyframeIndex = kNotSet; + if (!aAnimatedProperties.Equals(aPropertiesSetAtStart) && + !FindMatchingKeyframe(aKeyframes, 0.0, aInheritedTimingFunction, + startKeyframeIndex)) { + Keyframe newKeyframe; + newKeyframe.mOffset.emplace(0.0); + newKeyframe.mTimingFunction = aInheritedTimingFunction; + aKeyframes.InsertElementAt(startKeyframeIndex, Move(newKeyframe)); + } + + // Find/create the keyframe to add end values to + size_t endKeyframeIndex = kNotSet; + if (!aAnimatedProperties.Equals(aPropertiesSetAtEnd)) { + if (!FindMatchingKeyframe(Reversed(aKeyframes), 1.0, + aInheritedTimingFunction, endKeyframeIndex)) { + Keyframe newKeyframe; + newKeyframe.mOffset.emplace(1.0); + newKeyframe.mTimingFunction = aInheritedTimingFunction; + aKeyframes.AppendElement(Move(newKeyframe)); + endKeyframeIndex = aKeyframes.Length() - 1; + } else { + // endKeyframeIndex is currently a count from the end of the array + // so we need to reverse it. + endKeyframeIndex = aKeyframes.Length() - 1 - endKeyframeIndex; + } + } + + if (startKeyframeIndex == kNotSet && endKeyframeIndex == kNotSet) { + return; + } + + // Now that we have finished manipulating aKeyframes, it is safe to + // take pointers to its elements. + Keyframe* startKeyframe = startKeyframeIndex == kNotSet + ? nullptr : &aKeyframes[startKeyframeIndex]; + Keyframe* endKeyframe = endKeyframeIndex == kNotSet + ? nullptr : &aKeyframes[endKeyframeIndex]; + + // Iterate through all properties and fill-in missing values + for (nsCSSPropertyID prop = nsCSSPropertyID(0); + prop < eCSSProperty_COUNT_no_shorthands; + prop = nsCSSPropertyID(prop + 1)) { + if (!aAnimatedProperties.HasProperty(prop)) { + continue; + } + + if (startKeyframe && !aPropertiesSetAtStart.HasProperty(prop)) { + AppendProperty(aPresContext, prop, startKeyframe->mPropertyValues); + } + if (endKeyframe && !aPropertiesSetAtEnd.HasProperty(prop)) { + AppendProperty(aPresContext, prop, endKeyframe->mPropertyValues); + } + } +} + +void +CSSAnimationBuilder::AppendProperty( + nsPresContext* aPresContext, + nsCSSPropertyID aProperty, + nsTArray& aPropertyValues) +{ + PropertyValuePair propertyValue; + propertyValue.mProperty = aProperty; + propertyValue.mValue = GetComputedValue(aPresContext, aProperty); + + aPropertyValues.AppendElement(Move(propertyValue)); +} + +nsCSSValue +CSSAnimationBuilder::GetComputedValue(nsPresContext* aPresContext, + nsCSSPropertyID aProperty) +{ + nsCSSValue result; + StyleAnimationValue computedValue; + + if (!mStyleWithoutAnimation) { + MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(), + "ServoStyleSet should not use nsAnimationManager for " + "animations"); + mStyleWithoutAnimation = aPresContext->StyleSet()->AsGecko()-> + ResolveStyleWithoutAnimation(mTarget, mStyleContext, + eRestyle_AllHintsWithAnimations); + } + + if (StyleAnimationValue::ExtractComputedValue(aProperty, + mStyleWithoutAnimation, + computedValue)) { + DebugOnly uncomputeResult = + StyleAnimationValue::UncomputeValue(aProperty, Move(computedValue), + result); + MOZ_ASSERT(uncomputeResult, + "Unable to get specified value from computed value"); + } + + // If we hit this assertion, it probably means we are fetching a value from + // the computed style that we don't know how to represent as + // a StyleAnimationValue. + MOZ_ASSERT(result.GetUnit() != eCSSUnit_Null, "Got null computed value"); + + return result; +} + +void +nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext, + dom::Element* aTarget, + CSSAnimationCollection* aCollection, + OwningCSSAnimationPtrArray& aAnimations) +{ + MOZ_ASSERT(aAnimations.IsEmpty(), "expect empty array"); + + const nsStyleDisplay *disp = aStyleContext->StyleDisplay(); + + CSSAnimationBuilder builder(aStyleContext, aTarget, aCollection); + + for (size_t animIdx = disp->mAnimationNameCount; animIdx-- != 0;) { + const StyleAnimation& src = disp->mAnimations[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. + MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(), + "ServoStyleSet should not use nsAnimationManager for " + "animations"); + nsCSSKeyframesRule* rule = + src.GetName().IsEmpty() + ? nullptr + : mPresContext->StyleSet()->AsGecko()->KeyframesRuleForName(src.GetName()); + if (!rule) { + continue; + } + + RefPtr dest = builder.Build(mPresContext, src, rule); + dest->SetAnimationIndex(static_cast(animIdx)); + aAnimations.AppendElement(dest); + } +} + diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h new file mode 100644 index 000000000..abe3aeeb8 --- /dev/null +++ b/layout/style/nsAnimationManager.h @@ -0,0 +1,361 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 "mozilla/ContentEvents.h" +#include "mozilla/EventForwards.h" +#include "AnimationCommon.h" +#include "mozilla/dom/Animation.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/TimeStamp.h" + +class nsIGlobalObject; +class nsStyleContext; + +namespace mozilla { +namespace css { +class Declaration; +} /* namespace css */ +namespace dom { +class KeyframeEffectReadOnly; +class Promise; +} /* namespace dom */ + +enum class CSSPseudoElementType : uint8_t; + +struct AnimationEventInfo { + RefPtr mElement; + RefPtr mAnimation; + InternalAnimationEvent mEvent; + TimeStamp mTimeStamp; + + AnimationEventInfo(dom::Element* aElement, + CSSPseudoElementType aPseudoType, + EventMessage aMessage, + const nsSubstring& aAnimationName, + const StickyTimeDuration& aElapsedTime, + const TimeStamp& aTimeStamp, + dom::Animation* aAnimation) + : mElement(aElement) + , mAnimation(aAnimation) + , mEvent(true, aMessage) + , mTimeStamp(aTimeStamp) + { + // XXX Looks like nobody initialize WidgetEvent::time + mEvent.mAnimationName = aAnimationName; + mEvent.mElapsedTime = aElapsedTime.ToSeconds(); + mEvent.mPseudoElement = + AnimationCollection::PseudoTypeAsString(aPseudoType); + } + + // InternalAnimationEvent doesn't support copy-construction, so we need + // to ourselves in order to work with nsTArray + AnimationEventInfo(const AnimationEventInfo& aOther) + : mElement(aOther.mElement) + , mAnimation(aOther.mAnimation) + , mEvent(true, aOther.mEvent.mMessage) + , mTimeStamp(aOther.mTimeStamp) + { + mEvent.AssignAnimationEventData(aOther.mEvent, false); + } +}; + +namespace dom { + +class CSSAnimation final : public Animation +{ +public: + explicit CSSAnimation(nsIGlobalObject* aGlobal, + const nsSubstring& aAnimationName) + : dom::Animation(aGlobal) + , mAnimationName(aAnimationName) + , mIsStylePaused(false) + , mPauseShouldStick(false) + , mNeedsNewAnimationIndexWhenRun(false) + , mPreviousPhaseOrIteration(PREVIOUS_PHASE_BEFORE) + { + // We might need to drop this assertion once we add a script-accessible + // constructor but for animations generated from CSS markup the + // animation-name should never be empty. + MOZ_ASSERT(!mAnimationName.IsEmpty(), "animation-name should not be empty"); + } + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + CSSAnimation* AsCSSAnimation() override { return this; } + const CSSAnimation* AsCSSAnimation() const override { return this; } + + // CSSAnimation interface + void GetAnimationName(nsString& aRetVal) const { aRetVal = mAnimationName; } + + // Alternative to GetAnimationName that returns a reference to the member + // for more efficient internal usage. + const nsString& AnimationName() const { return mAnimationName; } + + // Animation interface overrides + virtual Promise* GetReady(ErrorResult& aRv) override; + virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior) override; + virtual void Pause(ErrorResult& aRv) override; + + virtual AnimationPlayState PlayStateFromJS() const override; + virtual void PlayFromJS(ErrorResult& aRv) override; + + void PlayFromStyle(); + void PauseFromStyle(); + void CancelFromStyle() override + { + mOwningElement = OwningElementRef(); + + // When an animation is disassociated with style it enters an odd state + // where its composite order is undefined until it first transitions + // out of the idle state. + // + // Even if the composite order isn't defined we don't want it to be random + // in case we need to determine the order to dispatch events associated + // with an animation in this state. To solve this we treat the animation as + // if it had been added to the end of the global animation list so that + // its sort order is defined. We'll update this index again once the + // animation leaves the idle state. + mAnimationIndex = sNextAnimationIndex++; + mNeedsNewAnimationIndexWhenRun = true; + + Animation::CancelFromStyle(); + } + + void Tick() override; + void QueueEvents(); + + bool IsStylePaused() const { return mIsStylePaused; } + + bool HasLowerCompositeOrderThan(const CSSAnimation& aOther) const; + + void SetAnimationIndex(uint64_t aIndex) + { + MOZ_ASSERT(IsTiedToMarkup()); + if (IsRelevant() && + mAnimationIndex != aIndex) { + nsNodeUtils::AnimationChanged(this); + PostUpdate(); + } + mAnimationIndex = aIndex; + } + + // Sets the owning element which is used for determining the composite + // order of CSSAnimation objects generated from CSS markup. + // + // @see mOwningElement + void SetOwningElement(const OwningElementRef& aElement) + { + mOwningElement = aElement; + } + // True for animations that are generated from CSS markup and continue to + // reflect changes to that markup. + bool IsTiedToMarkup() const { return mOwningElement.IsSet(); } + +protected: + virtual ~CSSAnimation() + { + MOZ_ASSERT(!mOwningElement.IsSet(), "Owning element should be cleared " + "before a CSS animation is destroyed"); + } + + // Animation overrides + void UpdateTiming(SeekFlag aSeekFlag, + SyncNotifyFlag aSyncNotifyFlag) override; + + // Returns the duration from the start of the animation's source effect's + // active interval to the point where the animation actually begins playback. + // This is zero unless the animation's source effect has a negative delay in + // which case it is the absolute value of that delay. + // This is used for setting the elapsedTime member of CSS AnimationEvents. + TimeDuration InitialAdvance() const { + return mEffect ? + std::max(TimeDuration(), mEffect->SpecifiedTiming().mDelay * -1) : + TimeDuration(); + } + + nsString mAnimationName; + + // The (pseudo-)element whose computed animation-name refers to this + // animation (if any). + // + // This is used for determining the relative composite order of animations + // generated from CSS markup. + // + // Typically this will be the same as the target element of the keyframe + // effect associated with this animation. However, it can differ in the + // following circumstances: + // + // a) If script removes or replaces the effect of this animation, + // b) If this animation is cancelled (e.g. by updating the + // animation-name property or removing the owning element from the + // document), + // c) If this object is generated from script using the CSSAnimation + // constructor. + // + // For (b) and (c) the owning element will return !IsSet(). + OwningElementRef mOwningElement; + + // When combining animation-play-state with play() / pause() the following + // behavior applies: + // 1. pause() is sticky and always overrides the underlying + // animation-play-state + // 2. If animation-play-state is 'paused', play() will temporarily override + // it until animation-play-state next becomes 'running'. + // 3. Calls to play() trigger finishing behavior but setting the + // animation-play-state to 'running' does not. + // + // This leads to five distinct states: + // + // A. Running + // B. Running and temporarily overriding animation-play-state: paused + // C. Paused and sticky overriding animation-play-state: running + // D. Paused and sticky overriding animation-play-state: paused + // E. Paused by animation-play-state + // + // C and D may seem redundant but they differ in how to respond to the + // sequence: call play(), set animation-play-state: paused. + // + // C will transition to A then E leaving the animation paused. + // D will transition to B then B leaving the animation running. + // + // A state transition chart is as follows: + // + // A | B | C | D | E + // --------------------------- + // play() A | B | A | B | B + // pause() C | D | C | D | D + // 'running' A | A | C | C | A + // 'paused' E | B | D | D | E + // + // The base class, Animation already provides a boolean value, + // mIsPaused which gives us two states. To this we add a further two booleans + // to represent the states as follows. + // + // A. Running + // (!mIsPaused; !mIsStylePaused; !mPauseShouldStick) + // B. Running and temporarily overriding animation-play-state: paused + // (!mIsPaused; mIsStylePaused; !mPauseShouldStick) + // C. Paused and sticky overriding animation-play-state: running + // (mIsPaused; !mIsStylePaused; mPauseShouldStick) + // D. Paused and sticky overriding animation-play-state: paused + // (mIsPaused; mIsStylePaused; mPauseShouldStick) + // E. Paused by animation-play-state + // (mIsPaused; mIsStylePaused; !mPauseShouldStick) + // + // (That leaves 3 combinations of the boolean values that we never set because + // they don't represent valid states.) + bool mIsStylePaused; + bool mPauseShouldStick; + + // When true, indicates that when this animation next leaves the idle state, + // its animation index should be updated. + bool mNeedsNewAnimationIndexWhenRun; + + enum { + PREVIOUS_PHASE_BEFORE = uint64_t(-1), + PREVIOUS_PHASE_AFTER = uint64_t(-2) + }; + // One of the PREVIOUS_PHASE_* constants, or an integer for the iteration + // whose start we last notified on. + uint64_t mPreviousPhaseOrIteration; +}; + +} /* namespace dom */ + +template <> +struct AnimationTypeTraits +{ + static nsIAtom* ElementPropertyAtom() + { + return nsGkAtoms::animationsProperty; + } + static nsIAtom* BeforePropertyAtom() + { + return nsGkAtoms::animationsOfBeforeProperty; + } + static nsIAtom* AfterPropertyAtom() + { + return nsGkAtoms::animationsOfAfterProperty; + } +}; + +} /* namespace mozilla */ + +class nsAnimationManager final + : public mozilla::CommonAnimationManager +{ +public: + explicit nsAnimationManager(nsPresContext *aPresContext) + : mozilla::CommonAnimationManager(aPresContext) + { + } + + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager) + + typedef mozilla::AnimationCollection + CSSAnimationCollection; + typedef nsTArray> + OwningCSSAnimationPtrArray; + + /** + * Update the set of animations on |aElement| based on |aStyleContext|. + * If necessary, this will notify the corresponding EffectCompositor so + * that it can update its animation rule. + * + * aStyleContext may be a style context for aElement or for its + * :before or :after pseudo-element. + */ + void UpdateAnimations(nsStyleContext* aStyleContext, + mozilla::dom::Element* aElement); + + /** + * Add a pending event. + */ + void QueueEvent(mozilla::AnimationEventInfo&& aEventInfo) + { + mEventDispatcher.QueueEvent( + mozilla::Forward(aEventInfo)); + } + + /** + * Dispatch any pending events. We accumulate animationend and + * animationiteration events only during refresh driver notifications + * (and dispatch them at the end of such notifications), but we + * accumulate animationstart events at other points when style + * contexts are created. + */ + void DispatchEvents() + { + RefPtr kungFuDeathGrip(this); + mEventDispatcher.DispatchEvents(mPresContext); + } + void SortEvents() { mEventDispatcher.SortEvents(); } + void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } + + // Stop animations on the element. This method takes the real element + // rather than the element for the generated content for animations on + // ::before and ::after. + void StopAnimationsForElement(mozilla::dom::Element* aElement, + mozilla::CSSPseudoElementType aPseudoType); + +protected: + ~nsAnimationManager() override = default; + +private: + + void BuildAnimations(nsStyleContext* aStyleContext, + mozilla::dom::Element* aTarget, + CSSAnimationCollection* aCollection, + OwningCSSAnimationPtrArray& aAnimations); + + mozilla::DelayedEventDispatcher mEventDispatcher; +}; + +#endif /* !defined(nsAnimationManager_h_) */ diff --git a/layout/style/nsCSSAnonBoxList.h b/layout/style/nsCSSAnonBoxList.h new file mode 100644 index 000000000..bb44215f1 --- /dev/null +++ b/layout/style/nsCSSAnonBoxList.h @@ -0,0 +1,103 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +/* + * This file contains the list of nsIAtoms 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 macro CSS_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 is the C++ identifier of + * the atom. The second argument is the string value of the atom. + */ + +// OUTPUT_CLASS=nsCSSAnonBoxes +// MACRO_NAME=CSS_ANON_BOX + +// ::-moz-text and ::-moz-other-non-element are non-elements which no +// rule will match. +CSS_ANON_BOX(mozText, ":-moz-text") +// This anonymous box has two uses: +// 1. placeholder frames, +// 2. nsFirstLetterFrames for content outside the ::first-letter. +CSS_ANON_BOX(mozOtherNonElement, ":-moz-other-non-element") + +CSS_ANON_BOX(mozAnonymousBlock, ":-moz-anonymous-block") +CSS_ANON_BOX(mozAnonymousPositionedBlock, ":-moz-anonymous-positioned-block") +CSS_ANON_BOX(mozMathMLAnonymousBlock, ":-moz-mathml-anonymous-block") +CSS_ANON_BOX(mozXULAnonymousBlock, ":-moz-xul-anonymous-block") + +// Framesets +CSS_ANON_BOX(horizontalFramesetBorder, ":-moz-hframeset-border") +CSS_ANON_BOX(verticalFramesetBorder, ":-moz-vframeset-border") + +CSS_ANON_BOX(mozLineFrame, ":-moz-line-frame") + +CSS_ANON_BOX(buttonContent, ":-moz-button-content") +CSS_ANON_BOX(mozButtonLabel, ":-moz-buttonlabel") +CSS_ANON_BOX(cellContent, ":-moz-cell-content") +CSS_ANON_BOX(dropDownList, ":-moz-dropdown-list") +CSS_ANON_BOX(fieldsetContent, ":-moz-fieldset-content") +CSS_ANON_BOX(framesetBlank, ":-moz-frameset-blank") +CSS_ANON_BOX(mozDisplayComboboxControlFrame, ":-moz-display-comboboxcontrol-frame") +CSS_ANON_BOX(htmlCanvasContent, ":-moz-html-canvas-content") + +CSS_ANON_BOX(inlineTable, ":-moz-inline-table") +CSS_ANON_BOX(table, ":-moz-table") +CSS_ANON_BOX(tableCell, ":-moz-table-cell") +CSS_ANON_BOX(tableColGroup, ":-moz-table-column-group") +CSS_ANON_BOX(tableCol, ":-moz-table-column") +CSS_ANON_BOX(tableWrapper, ":-moz-table-wrapper") +CSS_ANON_BOX(tableRowGroup, ":-moz-table-row-group") +CSS_ANON_BOX(tableRow, ":-moz-table-row") + +CSS_ANON_BOX(canvas, ":-moz-canvas") +CSS_ANON_BOX(pageBreak, ":-moz-pagebreak") +CSS_ANON_BOX(page, ":-moz-page") +CSS_ANON_BOX(pageContent, ":-moz-pagecontent") +CSS_ANON_BOX(pageSequence, ":-moz-page-sequence") +CSS_ANON_BOX(scrolledContent, ":-moz-scrolled-content") +CSS_ANON_BOX(scrolledCanvas, ":-moz-scrolled-canvas") +CSS_ANON_BOX(scrolledPageSequence, ":-moz-scrolled-page-sequence") +CSS_ANON_BOX(columnContent, ":-moz-column-content") +CSS_ANON_BOX(viewport, ":-moz-viewport") +CSS_ANON_BOX(viewportScroll, ":-moz-viewport-scroll") + +// Inside a flex container, a contiguous run of text gets wrapped in +// an anonymous block, which is then treated as a flex item. +CSS_ANON_BOX(anonymousFlexItem, ":-moz-anonymous-flex-item") + +// Inside a grid container, a contiguous run of text gets wrapped in +// an anonymous block, which is then treated as a grid item. +CSS_ANON_BOX(anonymousGridItem, ":-moz-anonymous-grid-item") + +CSS_ANON_BOX(ruby, ":-moz-ruby") +CSS_ANON_BOX(rubyBase, ":-moz-ruby-base") +CSS_ANON_BOX(rubyBaseContainer, ":-moz-ruby-base-container") +CSS_ANON_BOX(rubyText, ":-moz-ruby-text") +CSS_ANON_BOX(rubyTextContainer, ":-moz-ruby-text-container") + +#ifdef MOZ_XUL +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(moztreeprogressmeter, ":-moz-tree-progressmeter") +CSS_ANON_BOX(moztreedropfeedback, ":-moz-tree-drop-feedback") +#endif + +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") diff --git a/layout/style/nsCSSAnonBoxes.cpp b/layout/style/nsCSSAnonBoxes.cpp new file mode 100644 index 000000000..3585c9e38 --- /dev/null +++ b/layout/style/nsCSSAnonBoxes.cpp @@ -0,0 +1,52 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCSSAnonBoxes.h" +#include "nsAtomListUtils.h" +#include "nsStaticAtom.h" + +using namespace mozilla; + +// define storage for all atoms +#define CSS_ANON_BOX(_name, _value) \ + nsICSSAnonBoxPseudo* nsCSSAnonBoxes::_name; +#include "nsCSSAnonBoxList.h" +#undef CSS_ANON_BOX + +#define CSS_ANON_BOX(name_, value_) \ + NS_STATIC_ATOM_BUFFER(name_##_buffer, value_) +#include "nsCSSAnonBoxList.h" +#undef CSS_ANON_BOX + +static const nsStaticAtom CSSAnonBoxes_info[] = { +#define CSS_ANON_BOX(name_, value_) \ + NS_STATIC_ATOM(name_##_buffer, (nsIAtom**)&nsCSSAnonBoxes::name_), +#include "nsCSSAnonBoxList.h" +#undef CSS_ANON_BOX +}; + +void nsCSSAnonBoxes::AddRefAtoms() +{ + NS_RegisterStaticAtoms(CSSAnonBoxes_info); +} + +bool nsCSSAnonBoxes::IsAnonBox(nsIAtom *aAtom) +{ + return nsAtomListUtils::IsMember(aAtom, CSSAnonBoxes_info, + ArrayLength(CSSAnonBoxes_info)); +} + +#ifdef MOZ_XUL +/* static */ bool +nsCSSAnonBoxes::IsTreePseudoElement(nsIAtom* aPseudo) +{ + return StringBeginsWith(nsDependentAtomString(aPseudo), + NS_LITERAL_STRING(":-moz-tree-")); +} +#endif diff --git a/layout/style/nsCSSAnonBoxes.h b/layout/style/nsCSSAnonBoxes.h new file mode 100644 index 000000000..f631f8801 --- /dev/null +++ b/layout/style/nsCSSAnonBoxes.h @@ -0,0 +1,34 @@ +/* -*- 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/. */ + +/* atom list for CSS anonymous boxes */ + +#ifndef nsCSSAnonBoxes_h___ +#define nsCSSAnonBoxes_h___ + +#include "nsIAtom.h" + +// Empty class derived from nsIAtom so that function signatures can +// require an atom from this atom list. +class nsICSSAnonBoxPseudo : public nsIAtom {}; + +class nsCSSAnonBoxes { +public: + + static void AddRefAtoms(); + + static bool IsAnonBox(nsIAtom *aAtom); +#ifdef MOZ_XUL + static bool IsTreePseudoElement(nsIAtom* aPseudo); +#endif + static bool IsNonElement(nsIAtom* aPseudo) + { return aPseudo == mozText || aPseudo == mozOtherNonElement; } + +#define CSS_ANON_BOX(_name, _value) static nsICSSAnonBoxPseudo* _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 000000000..03431b469 --- /dev/null +++ b/layout/style/nsCSSCounterDescList.h @@ -0,0 +1,15 @@ +/* -*- 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/. */ + +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/nsCSSDataBlock.cpp b/layout/style/nsCSSDataBlock.cpp new file mode 100644 index 000000000..fe2dc621a --- /dev/null +++ b/layout/style/nsCSSDataBlock.cpp @@ -0,0 +1,802 @@ +/* -*- 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/. */ + +/* + * compact representation of the property-value pairs within a CSS + * declaration, and the code for expanding and compacting it + */ + +#include "nsCSSDataBlock.h" + +#include "CSSVariableImageTable.h" +#include "mozilla/css/Declaration.h" +#include "mozilla/css/ImageLoader.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/WritingModes.h" +#include "nsAutoPtr.h" +#include "nsIDocument.h" +#include "nsRuleData.h" +#include "nsStyleContext.h" +#include "nsStyleSet.h" + +using namespace mozilla; +using namespace mozilla::css; + +/** + * Does a fast move of aSource to aDest. The previous value in + * aDest is cleanly destroyed, and aSource is cleared. Returns + * true if, before the copy, the value at aSource compared unequal + * to the value at aDest; false otherwise. + */ +static bool +MoveValue(nsCSSValue* aSource, nsCSSValue* aDest) +{ + bool changed = (*aSource != *aDest); + aDest->~nsCSSValue(); + memcpy(aDest, aSource, sizeof(nsCSSValue)); + new (aSource) nsCSSValue(); + return changed; +} + +static bool +ShouldIgnoreColors(nsRuleData *aRuleData) +{ + return aRuleData->mLevel != SheetType::Agent && + aRuleData->mLevel != SheetType::User && + !aRuleData->mPresContext->UseDocumentColors(); +} + +/** + * Tries to call |nsCSSValue::StartImageLoad()| on an image source. + * Image sources are specified by |url()| or |-moz-image-rect()| function. + */ +static void +TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument, + nsStyleContext* aContext, nsCSSPropertyID aProperty, + bool aForTokenStream) +{ + MOZ_ASSERT(aDocument); + + if (aValue.GetUnit() == eCSSUnit_URL) { +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + // The 'mask-image' property accepts local reference URIs. + // For example, + // mask-image: url(#mask_id); // refer to a SVG mask element, whose id is + // // "mask_id", in the current document. + // For such 'mask-image' values (pointing to an in-document element), + // there is no need to trigger image download. + if (aProperty == eCSSProperty_mask_image) { + // Filter out all fragment URLs. + // Since nsCSSValue::GetURLStructValue runs much faster than + // nsIURI::EqualsExceptRef bellow, we get performance gain by this + // early return. + URLValue* urlValue = aValue.GetURLStructValue(); + if (urlValue->IsLocalRef()) { + return; + } + + // Even though urlValue is not a fragment URL, it might still refer to + // an internal resource. + // For example, aDocument base URL is "http://foo/index.html" and + // intentionally references a mask-image at + // url(http://foo/index.html#mask) which still refers to a resource in + // aDocument. + nsIURI* imageURI = aValue.GetURLValue(); + if (imageURI) { + nsIURI* docURI = aDocument->GetDocumentURI(); + bool isEqualExceptRef = false; + nsresult rv = imageURI->EqualsExceptRef(docURI, &isEqualExceptRef); + if (NS_SUCCEEDED(rv) && isEqualExceptRef) { + return; + } + } + } +#endif + aValue.StartImageLoad(aDocument); + if (aForTokenStream && aContext) { + CSSVariableImageTable::Add(aContext, aProperty, + aValue.GetImageStructValue()); + } + } + else if (aValue.GetUnit() == eCSSUnit_Image) { + // If we already have a request, see if this document needs to clone it. + imgIRequest* request = aValue.GetImageValue(nullptr); + + if (request) { + ImageValue* imageValue = aValue.GetImageStructValue(); + aDocument->StyleImageLoader()->MaybeRegisterCSSImage(imageValue); + if (aForTokenStream && aContext) { + CSSVariableImageTable::Add(aContext, aProperty, imageValue); + } + } + } + else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) { + nsCSSValue::Array* arguments = aValue.GetArrayValue(); + MOZ_ASSERT(arguments->Count() == 6, "unexpected num of arguments"); + + const nsCSSValue& image = arguments->Item(1); + TryToStartImageLoadOnValue(image, aDocument, aContext, aProperty, + aForTokenStream); + } +} + +static void +TryToStartImageLoad(const nsCSSValue& aValue, nsIDocument* aDocument, + nsStyleContext* aContext, nsCSSPropertyID aProperty, + bool aForTokenStream) +{ + if (aValue.GetUnit() == eCSSUnit_List) { + for (const nsCSSValueList* l = aValue.GetListValue(); l; l = l->mNext) { + TryToStartImageLoad(l->mValue, aDocument, aContext, aProperty, + aForTokenStream); + } + } else if (nsCSSProps::PropHasFlags(aProperty, + CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0)) { + if (aValue.GetUnit() == eCSSUnit_Array) { + TryToStartImageLoadOnValue(aValue.GetArrayValue()->Item(0), aDocument, + aContext, aProperty, aForTokenStream); + } + } else { + TryToStartImageLoadOnValue(aValue, aDocument, aContext, aProperty, + aForTokenStream); + } +} + +static inline bool +ShouldStartImageLoads(nsRuleData *aRuleData, nsCSSPropertyID aProperty) +{ + // Don't initiate image loads for if-visited styles. This is + // important because: + // (1) it's a waste of CPU and bandwidth + // (2) in some cases we'd start the image load on a style change + // where we wouldn't have started the load initially, which makes + // which links are visited detectable to Web pages (see bug + // 557287) + return !aRuleData->mStyleContext->IsStyleIfVisited() && + nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_START_IMAGE_LOADS); +} + +static void +MapSinglePropertyInto(nsCSSPropertyID aTargetProp, + const nsCSSValue* aSrcValue, + nsCSSValue* aTargetValue, + nsRuleData* aRuleData) +{ + MOZ_ASSERT(!nsCSSProps::PropHasFlags(aTargetProp, CSS_PROPERTY_LOGICAL), + "Can't map into a logical property"); + MOZ_ASSERT(aSrcValue->GetUnit() != eCSSUnit_Null, "oops"); + + // Although aTargetValue is the nsCSSValue we are going to write into, + // we also look at its value before writing into it. This is done + // when aTargetValue is a token stream value, which is the case when we + // have just re-parsed a property that had a variable reference (in + // nsCSSParser::ParsePropertyWithVariableReferences). TryToStartImageLoad + // then records any resulting ImageValue objects in the + // CSSVariableImageTable, to give them the appropriate lifetime. + MOZ_ASSERT(aTargetValue->GetUnit() == eCSSUnit_TokenStream || + aTargetValue->GetUnit() == eCSSUnit_Null, + "aTargetValue must only be a token stream (when re-parsing " + "properties with variable references) or null"); + + if (ShouldStartImageLoads(aRuleData, aTargetProp)) { + nsIDocument* doc = aRuleData->mPresContext->Document(); + TryToStartImageLoad(*aSrcValue, doc, aRuleData->mStyleContext, + aTargetProp, + aTargetValue->GetUnit() == eCSSUnit_TokenStream); + } + *aTargetValue = *aSrcValue; + if (nsCSSProps::PropHasFlags(aTargetProp, + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) && + ShouldIgnoreColors(aRuleData)) + { + if (aTargetProp == eCSSProperty_background_color) { + // Force non-'transparent' background + // colors to the user's default. + if (aTargetValue->IsNonTransparentColor()) { + aTargetValue->SetColorValue(aRuleData->mPresContext-> + DefaultBackgroundColor()); + } + } else { + // Ignore 'color', 'border-*-color', etc. + *aTargetValue = nsCSSValue(); + } + } +} + +/** + * If aProperty is a logical property, converts it to the equivalent physical + * property based on writing mode information obtained from aRuleData's + * style context. + */ +static inline void +EnsurePhysicalProperty(nsCSSPropertyID& aProperty, nsRuleData* aRuleData) +{ + bool isAxisProperty = + nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_AXIS); + bool isBlock = + nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_BLOCK_AXIS); + + int index; + + if (isAxisProperty) { + LogicalAxis logicalAxis = isBlock ? eLogicalAxisBlock : eLogicalAxisInline; + uint8_t wm = aRuleData->mStyleContext->StyleVisibility()->mWritingMode; + PhysicalAxis axis = + WritingMode::PhysicalAxisForLogicalAxis(wm, logicalAxis); + + // We rely on physical axis constants values matching the order of the + // physical properties in the logical group array. + static_assert(eAxisVertical == 0 && eAxisHorizontal == 1, + "unexpected axis constant values"); + index = axis; + } else { + bool isEnd = + nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL_END_EDGE); + + LogicalEdge edge = isEnd ? eLogicalEdgeEnd : eLogicalEdgeStart; + + // We handle block axis logical properties separately to save a bit of + // work that the WritingMode constructor does that is unnecessary + // unless we have an inline axis property. + mozilla::css::Side side; + if (isBlock) { + uint8_t wm = aRuleData->mStyleContext->StyleVisibility()->mWritingMode; + side = WritingMode::PhysicalSideForBlockAxis(wm, edge); + } else { + WritingMode wm(aRuleData->mStyleContext); + side = wm.PhysicalSideForInlineAxis(edge); + } + + // We rely on the physical side constant values matching the order of + // the physical properties in the logical group array. + static_assert(NS_SIDE_TOP == 0 && NS_SIDE_RIGHT == 1 && + NS_SIDE_BOTTOM == 2 && NS_SIDE_LEFT == 3, + "unexpected side constant values"); + index = side; + } + + const nsCSSPropertyID* props = nsCSSProps::LogicalGroup(aProperty); + size_t len = isAxisProperty ? 2 : 4; +#ifdef DEBUG + for (size_t i = 0; i < len; i++) { + MOZ_ASSERT(props[i] != eCSSProperty_UNKNOWN, + "unexpected logical group length"); + } + MOZ_ASSERT(props[len] == eCSSProperty_UNKNOWN, + "unexpected logical group length"); +#endif + + for (size_t i = 0; i < len; i++) { + if (aRuleData->ValueFor(props[i])->GetUnit() == eCSSUnit_Null) { + // A declaration of one of the logical properties in this logical + // group (but maybe not aProperty) would be the winning + // declaration in the cascade. This means that it's reasonably + // likely that this logical property could be the winning + // declaration in the cascade for some values of writing-mode, + // direction, and text-orientation. (It doesn't mean that for + // sure, though. For example, if this is a block-start logical + // property, and all but the bottom physical property were set. + // But the common case we want to hit here is logical declarations + // that are completely overridden by a shorthand.) + // + // If this logical property could be the winning declaration in + // the cascade for some values of writing-mode, direction, and + // text-orientation, then we have to fault the resulting style + // struct out of the rule tree. We can't cache anything on the + // rule tree if it depends on data from the style context, since + // data cached in the rule tree could be used with a style context + // with a different value of the depended-upon data. + uint8_t wm = WritingMode(aRuleData->mStyleContext).GetBits(); + aRuleData->mConditions.SetWritingModeDependency(wm); + break; + } + } + + aProperty = props[index]; +} + +void +nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const +{ + // If we have no data for these structs, then return immediately. + // This optimization should make us return most of the time, so we + // have to worry much less (although still some) about the speed of + // the rest of the function. + if (!(aRuleData->mSIDs & mStyleBits)) + return; + + // We process these in reverse order so that we end up mapping the + // right property when one can be expressed using both logical and + // physical property names. + for (uint32_t i = mNumProps; i-- > 0; ) { + nsCSSPropertyID iProp = PropertyAtIndex(i); + if (nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]) & + aRuleData->mSIDs) { + if (nsCSSProps::PropHasFlags(iProp, CSS_PROPERTY_LOGICAL)) { + EnsurePhysicalProperty(iProp, aRuleData); + } + nsCSSValue* target = aRuleData->ValueFor(iProp); + if (target->GetUnit() == eCSSUnit_Null) { + const nsCSSValue *val = ValueAtIndex(i); + // In order for variable resolution to have the right information + // about the stylesheet level of a value, that level needs to be + // stored on the token stream. We can't do that at creation time + // because the CSS parser (which creates the object) has no idea + // about the stylesheet level, so we do it here instead, where + // the rule walking will have just updated aRuleData. + if (val->GetUnit() == eCSSUnit_TokenStream) { + val->GetTokenStreamValue()->mLevel = aRuleData->mLevel; + } + MapSinglePropertyInto(iProp, val, target, aRuleData); + } + } + } +} + +const nsCSSValue* +nsCSSCompressedDataBlock::ValueFor(nsCSSPropertyID aProperty) const +{ + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), + "Don't call for shorthands"); + + // If we have no data for this struct, then return immediately. + // This optimization should make us return most of the time, so we + // have to worry much less (although still some) about the speed of + // the rest of the function. + if (!(nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]) & + mStyleBits)) + return nullptr; + + for (uint32_t i = 0; i < mNumProps; i++) { + if (PropertyAtIndex(i) == aProperty) { + return ValueAtIndex(i); + } + } + + return nullptr; +} + +bool +nsCSSCompressedDataBlock::TryReplaceValue(nsCSSPropertyID aProperty, + nsCSSExpandedDataBlock& aFromBlock, + bool *aChanged) +{ + nsCSSValue* newValue = aFromBlock.PropertyAt(aProperty); + MOZ_ASSERT(newValue && newValue->GetUnit() != eCSSUnit_Null, + "cannot replace with empty value"); + + const nsCSSValue* oldValue = ValueFor(aProperty); + if (!oldValue) { + *aChanged = false; + return false; + } + + *aChanged = MoveValue(newValue, const_cast(oldValue)); + aFromBlock.ClearPropertyBit(aProperty); + return true; +} + +nsCSSCompressedDataBlock* +nsCSSCompressedDataBlock::Clone() const +{ + nsAutoPtr + result(new(mNumProps) nsCSSCompressedDataBlock(mNumProps)); + + result->mStyleBits = mStyleBits; + + for (uint32_t i = 0; i < mNumProps; i++) { + result->SetPropertyAtIndex(i, PropertyAtIndex(i)); + result->CopyValueToIndex(i, ValueAtIndex(i)); + } + + return result.forget(); +} + +nsCSSCompressedDataBlock::~nsCSSCompressedDataBlock() +{ + for (uint32_t i = 0; i < mNumProps; i++) { +#ifdef DEBUG + (void)PropertyAtIndex(i); // this checks the property is in range +#endif + const nsCSSValue* val = ValueAtIndex(i); + MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null, "oops"); + val->~nsCSSValue(); + } +} + +/* static */ nsCSSCompressedDataBlock* +nsCSSCompressedDataBlock::CreateEmptyBlock() +{ + nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock(0); + return result; +} + +size_t +nsCSSCompressedDataBlock::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + for (uint32_t i = 0; i < mNumProps; i++) { + n += ValueAtIndex(i)->SizeOfExcludingThis(aMallocSizeOf); + } + return n; +} + +bool +nsCSSCompressedDataBlock::HasDefaultBorderImageSlice() const +{ + const nsCSSValueList *slice = + ValueFor(eCSSProperty_border_image_slice)->GetListValue(); + return !slice->mNext && + slice->mValue.GetRectValue().AllSidesEqualTo( + nsCSSValue(1.0f, eCSSUnit_Percent)); +} + +bool +nsCSSCompressedDataBlock::HasDefaultBorderImageWidth() const +{ + const nsCSSRect &width = + ValueFor(eCSSProperty_border_image_width)->GetRectValue(); + return width.AllSidesEqualTo(nsCSSValue(1.0f, eCSSUnit_Number)); +} + +bool +nsCSSCompressedDataBlock::HasDefaultBorderImageOutset() const +{ + const nsCSSRect &outset = + ValueFor(eCSSProperty_border_image_outset)->GetRectValue(); + return outset.AllSidesEqualTo(nsCSSValue(0.0f, eCSSUnit_Number)); +} + +bool +nsCSSCompressedDataBlock::HasDefaultBorderImageRepeat() const +{ + const nsCSSValuePair &repeat = + ValueFor(eCSSProperty_border_image_repeat)->GetPairValue(); + return repeat.BothValuesEqualTo( + nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, eCSSUnit_Enumerated)); +} + +/*****************************************************************************/ + +nsCSSExpandedDataBlock::nsCSSExpandedDataBlock() +{ + AssertInitialState(); +} + +nsCSSExpandedDataBlock::~nsCSSExpandedDataBlock() +{ + AssertInitialState(); +} + +void +nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock, + bool aImportant) +{ + /* + * Save needless copying and allocation by copying the memory + * corresponding to the stored data in the compressed block. + */ + for (uint32_t i = 0; i < aBlock->mNumProps; i++) { + nsCSSPropertyID iProp = aBlock->PropertyAtIndex(i); + MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range"); + MOZ_ASSERT(!HasPropertyBit(iProp), + "compressed block has property multiple times"); + SetPropertyBit(iProp); + if (aImportant) + SetImportantBit(iProp); + + const nsCSSValue* val = aBlock->ValueAtIndex(i); + nsCSSValue* dest = PropertyAt(iProp); + MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null, "oops"); + MOZ_ASSERT(dest->GetUnit() == eCSSUnit_Null, + "expanding into non-empty block"); +#ifdef NS_BUILD_REFCNT_LOGGING + dest->~nsCSSValue(); +#endif + memcpy(dest, val, sizeof(nsCSSValue)); + } + + // Set the number of properties to zero so that we don't destroy the + // remnants of what we just copied. + aBlock->SetNumPropsToZero(); + delete aBlock; +} + +void +nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock *aNormalBlock, + nsCSSCompressedDataBlock *aImportantBlock) +{ + MOZ_ASSERT(aNormalBlock, "unexpected null block"); + AssertInitialState(); + + DoExpand(aNormalBlock, false); + if (aImportantBlock) { + DoExpand(aImportantBlock, true); + } +} + +void +nsCSSExpandedDataBlock::ComputeNumProps(uint32_t* aNumPropsNormal, + uint32_t* aNumPropsImportant) +{ + *aNumPropsNormal = *aNumPropsImportant = 0; + for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; ++iHigh) { + if (!mPropertiesSet.HasPropertyInChunk(iHigh)) + continue; + for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; ++iLow) { + if (!mPropertiesSet.HasPropertyAt(iHigh, iLow)) + continue; +#ifdef DEBUG + nsCSSPropertyID iProp = nsCSSPropertyIDSet::CSSPropertyAt(iHigh, iLow); +#endif + MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range"); + MOZ_ASSERT(PropertyAt(iProp)->GetUnit() != eCSSUnit_Null, + "null value while computing size"); + if (mPropertiesImportant.HasPropertyAt(iHigh, iLow)) + (*aNumPropsImportant)++; + else + (*aNumPropsNormal)++; + } + } +} + +void +nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock, + nsCSSCompressedDataBlock **aImportantBlock, + const nsTArray& aOrder) +{ + nsAutoPtr result_normal, result_important; + uint32_t i_normal = 0, i_important = 0; + + uint32_t numPropsNormal, numPropsImportant; + ComputeNumProps(&numPropsNormal, &numPropsImportant); + + result_normal = + new(numPropsNormal) nsCSSCompressedDataBlock(numPropsNormal); + + if (numPropsImportant != 0) { + result_important = + new(numPropsImportant) nsCSSCompressedDataBlock(numPropsImportant); + } else { + result_important = nullptr; + } + + /* + * Save needless copying and allocation by copying the memory + * corresponding to the stored data in the expanded block, and then + * clearing the data in the expanded block. + */ + for (size_t i = 0; i < aOrder.Length(); i++) { + nsCSSPropertyID iProp = static_cast(aOrder[i]); + if (iProp >= eCSSProperty_COUNT) { + // a custom property + continue; + } + MOZ_ASSERT(mPropertiesSet.HasProperty(iProp), + "aOrder identifies a property not in the expanded " + "data block"); + MOZ_ASSERT(!nsCSSProps::IsShorthand(iProp), "out of range"); + bool important = mPropertiesImportant.HasProperty(iProp); + nsCSSCompressedDataBlock *result = + important ? result_important : result_normal; + uint32_t* ip = important ? &i_important : &i_normal; + nsCSSValue* val = PropertyAt(iProp); + MOZ_ASSERT(val->GetUnit() != eCSSUnit_Null, + "Null value while compressing"); + result->SetPropertyAtIndex(*ip, iProp); + result->RawCopyValueToIndex(*ip, val); + new (val) nsCSSValue(); + (*ip)++; + result->mStyleBits |= + nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[iProp]); + } + + MOZ_ASSERT(numPropsNormal == i_normal, "bad numProps"); + + if (result_important) { + MOZ_ASSERT(numPropsImportant == i_important, "bad numProps"); + } + +#ifdef DEBUG + { + // assert that we didn't have any other properties on this expanded data + // block that we didn't find in aOrder + uint32_t numPropsInSet = 0; + for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; iHigh++) { + if (!mPropertiesSet.HasPropertyInChunk(iHigh)) { + continue; + } + for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; iLow++) { + if (mPropertiesSet.HasPropertyAt(iHigh, iLow)) { + numPropsInSet++; + } + } + } + MOZ_ASSERT(numPropsNormal + numPropsImportant == numPropsInSet, + "aOrder missing properties from the expanded data block"); + } +#endif + + ClearSets(); + AssertInitialState(); + *aNormalBlock = result_normal.forget(); + *aImportantBlock = result_important.forget(); +} + +void +nsCSSExpandedDataBlock::AddLonghandProperty(nsCSSPropertyID aProperty, + const nsCSSValue& aValue) +{ + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), + "property out of range"); + nsCSSValue& storage = *static_cast(PropertyAt(aProperty)); + storage = aValue; + SetPropertyBit(aProperty); +} + +void +nsCSSExpandedDataBlock::Clear() +{ + for (size_t iHigh = 0; iHigh < nsCSSPropertyIDSet::kChunkCount; ++iHigh) { + if (!mPropertiesSet.HasPropertyInChunk(iHigh)) + continue; + for (size_t iLow = 0; iLow < nsCSSPropertyIDSet::kBitsInChunk; ++iLow) { + if (!mPropertiesSet.HasPropertyAt(iHigh, iLow)) + continue; + nsCSSPropertyID iProp = nsCSSPropertyIDSet::CSSPropertyAt(iHigh, iLow); + ClearLonghandProperty(iProp); + } + } + + AssertInitialState(); +} + +void +nsCSSExpandedDataBlock::ClearProperty(nsCSSPropertyID aPropID) +{ + if (nsCSSProps::IsShorthand(aPropID)) { + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES( + p, aPropID, CSSEnabledState::eIgnoreEnabledState) { + ClearLonghandProperty(*p); + } + } else { + ClearLonghandProperty(aPropID); + } +} + +void +nsCSSExpandedDataBlock::ClearLonghandProperty(nsCSSPropertyID aPropID) +{ + MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID), "out of range"); + + ClearPropertyBit(aPropID); + ClearImportantBit(aPropID); + PropertyAt(aPropID)->Reset(); +} + +bool +nsCSSExpandedDataBlock::TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, + nsCSSPropertyID aPropID, + CSSEnabledState aEnabledState, + bool aIsImportant, + bool aOverrideImportant, + bool aMustCallValueAppended, + css::Declaration* aDeclaration, + nsIDocument* aSheetDocument) +{ + if (!nsCSSProps::IsShorthand(aPropID)) { + return DoTransferFromBlock(aFromBlock, aPropID, + aIsImportant, aOverrideImportant, + aMustCallValueAppended, aDeclaration, + aSheetDocument); + } + + // We can pass CSSEnabledState::eIgnore (here, and in ClearProperty + // above) rather than a value corresponding to whether we're parsing + // a UA style sheet or certified app because we assert in nsCSSProps:: + // AddRefTable that shorthand properties available in these contexts + // also have all of their subproperties available in these contexts. + bool changed = false; + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, aEnabledState) { + changed |= DoTransferFromBlock(aFromBlock, *p, + aIsImportant, aOverrideImportant, + aMustCallValueAppended, aDeclaration, + aSheetDocument); + } + return changed; +} + +bool +nsCSSExpandedDataBlock::DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, + nsCSSPropertyID aPropID, + bool aIsImportant, + bool aOverrideImportant, + bool aMustCallValueAppended, + css::Declaration* aDeclaration, + nsIDocument* aSheetDocument) +{ + bool changed = false; + MOZ_ASSERT(aFromBlock.HasPropertyBit(aPropID), "oops"); + if (aIsImportant) { + if (!HasImportantBit(aPropID)) + changed = true; + SetImportantBit(aPropID); + } else { + if (HasImportantBit(aPropID)) { + // When parsing a declaration block, an !important declaration + // is not overwritten by an ordinary declaration of the same + // property later in the block. However, CSSOM manipulations + // come through here too, and in that case we do want to + // overwrite the property. + if (!aOverrideImportant) { + aFromBlock.ClearLonghandProperty(aPropID); + return false; + } + changed = true; + ClearImportantBit(aPropID); + } + } + + if (aMustCallValueAppended || !HasPropertyBit(aPropID)) { + aDeclaration->ValueAppended(aPropID); + } + + if (aSheetDocument) { + UseCounter useCounter = nsCSSProps::UseCounterFor(aPropID); + if (useCounter != eUseCounter_UNKNOWN) { + aSheetDocument->SetDocumentAndPageUseCounter(useCounter); + } + } + + SetPropertyBit(aPropID); + aFromBlock.ClearPropertyBit(aPropID); + + /* + * Save needless copying and allocation by calling the destructor in + * the destination, copying memory directly, and then using placement + * new. + */ + changed |= MoveValue(aFromBlock.PropertyAt(aPropID), PropertyAt(aPropID)); + return changed; +} + +void +nsCSSExpandedDataBlock::MapRuleInfoInto(nsCSSPropertyID aPropID, + nsRuleData* aRuleData) const +{ + MOZ_ASSERT(!nsCSSProps::IsShorthand(aPropID)); + + const nsCSSValue* src = PropertyAt(aPropID); + MOZ_ASSERT(src->GetUnit() != eCSSUnit_Null); + + nsCSSPropertyID physicalProp = aPropID; + if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_LOGICAL)) { + EnsurePhysicalProperty(physicalProp, aRuleData); + } + + nsCSSValue* dest = aRuleData->ValueFor(physicalProp); + MOZ_ASSERT(dest->GetUnit() == eCSSUnit_TokenStream && + dest->GetTokenStreamValue()->mPropertyID == aPropID); + + CSSVariableImageTable::ReplaceAll(aRuleData->mStyleContext, aPropID, [=] { + MapSinglePropertyInto(physicalProp, src, dest, aRuleData); + }); +} + +#ifdef DEBUG +void +nsCSSExpandedDataBlock::DoAssertInitialState() +{ + mPropertiesSet.AssertIsEmpty("not initial state"); + mPropertiesImportant.AssertIsEmpty("not initial state"); + + for (uint32_t i = 0; i < eCSSProperty_COUNT_no_shorthands; ++i) { + nsCSSPropertyID prop = nsCSSPropertyID(i); + MOZ_ASSERT(PropertyAt(prop)->GetUnit() == eCSSUnit_Null, + "not initial state"); + } +} +#endif diff --git a/layout/style/nsCSSDataBlock.h b/layout/style/nsCSSDataBlock.h new file mode 100644 index 000000000..76deaa911 --- /dev/null +++ b/layout/style/nsCSSDataBlock.h @@ -0,0 +1,372 @@ +/* -*- 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/. */ + +/* + * compact representation of the property-value pairs within a CSS + * declaration, and the code for expanding and compacting it + */ + +#ifndef nsCSSDataBlock_h__ +#define nsCSSDataBlock_h__ + +#include "mozilla/MemoryReporting.h" +#include "nsCSSProps.h" +#include "nsCSSPropertyIDSet.h" +#include "nsCSSValue.h" +#include "nsStyleStruct.h" +#include "imgRequestProxy.h" + +struct nsRuleData; +class nsCSSExpandedDataBlock; +class nsIDocument; + +namespace mozilla { +namespace css { +class Declaration; +} // namespace css +} // namespace mozilla + +/** + * An |nsCSSCompressedDataBlock| holds a usually-immutable chunk of + * property-value data for a CSS declaration block (which we misname a + * |css::Declaration|). Mutation is accomplished through + * |nsCSSExpandedDataBlock| or in some cases via direct slot access. + */ +class nsCSSCompressedDataBlock +{ +private: + friend class nsCSSExpandedDataBlock; + + // Only this class (via |CreateEmptyBlock|) or nsCSSExpandedDataBlock + // (in |Compress|) can create compressed data blocks. + explicit nsCSSCompressedDataBlock(uint32_t aNumProps) + : mStyleBits(0), mNumProps(aNumProps) + {} + +public: + ~nsCSSCompressedDataBlock(); + + /** + * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style + * rule using this block for storage. + */ + void MapRuleInfoInto(nsRuleData *aRuleData) const; + + /** + * Return the location at which the *value* for the property is + * stored, or null if the block does not contain a value for the + * property. + * + * Inefficient (by design). + * + * Must not be called for shorthands. + */ + const nsCSSValue* ValueFor(nsCSSPropertyID aProperty) const; + + /** + * Attempt to replace the value for |aProperty| stored in this block + * with the matching value stored in |aFromBlock|. + * This method will fail (returning false) if |aProperty| is not + * already in this block. It will set |aChanged| to true if it + * actually made a change to the block, but regardless, if it + * returns true, the value in |aFromBlock| was erased. + */ + bool TryReplaceValue(nsCSSPropertyID aProperty, + nsCSSExpandedDataBlock& aFromBlock, + bool* aChanged); + + /** + * Clone this block, or return null on out-of-memory. + */ + nsCSSCompressedDataBlock* Clone() const; + + /** + * Create a new nsCSSCompressedDataBlock holding no declarations. + */ + static nsCSSCompressedDataBlock* CreateEmptyBlock(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + + bool HasDefaultBorderImageSlice() const; + bool HasDefaultBorderImageWidth() const; + bool HasDefaultBorderImageOutset() const; + bool HasDefaultBorderImageRepeat() const; + + bool HasInheritedStyleData() const + { + return mStyleBits & NS_STYLE_INHERITED_STRUCT_MASK; + } + +private: + void* operator new(size_t aBaseSize, uint32_t aNumProps) { + MOZ_ASSERT(aBaseSize == sizeof(nsCSSCompressedDataBlock), + "unexpected size for nsCSSCompressedDataBlock"); + return ::operator new(aBaseSize + DataSize(aNumProps)); + } + +public: + // Ideally, |nsCSSPropertyID| would be |enum nsCSSPropertyID : int16_t|. But + // not all of the compilers we use are modern enough to support small + // enums. So we manually squeeze nsCSSPropertyID into 16 bits ourselves. + // The static assertion below ensures it fits. + typedef int16_t CompressedCSSProperty; + static const size_t MaxCompressedCSSProperty = INT16_MAX; + +private: + static size_t DataSize(uint32_t aNumProps) { + return size_t(aNumProps) * + (sizeof(nsCSSValue) + sizeof(CompressedCSSProperty)); + } + + int32_t mStyleBits; // the structs for which we have data, according to + // |nsCachedStyleData::GetBitForSID|. + uint32_t mNumProps; + // nsCSSValue elements are stored after these fields, and + // nsCSSPropertyID elements are stored -- each one compressed as a + // CompressedCSSProperty -- after the nsCSSValue elements. Space for them + // is allocated in |operator new| above. The static assertions following + // this class make sure that the value and property elements are aligned + // appropriately. + + nsCSSValue* Values() const { + return (nsCSSValue*)(this + 1); + } + + CompressedCSSProperty* CompressedProperties() const { + return (CompressedCSSProperty*)(Values() + mNumProps); + } + + nsCSSValue* ValueAtIndex(uint32_t i) const { + MOZ_ASSERT(i < mNumProps, "value index out of range"); + return Values() + i; + } + + nsCSSPropertyID PropertyAtIndex(uint32_t i) const { + MOZ_ASSERT(i < mNumProps, "property index out of range"); + nsCSSPropertyID prop = (nsCSSPropertyID)CompressedProperties()[i]; + MOZ_ASSERT(!nsCSSProps::IsShorthand(prop), "out of range"); + return prop; + } + + void CopyValueToIndex(uint32_t i, nsCSSValue* aValue) { + new (ValueAtIndex(i)) nsCSSValue(*aValue); + } + + void RawCopyValueToIndex(uint32_t i, nsCSSValue* aValue) { + memcpy(ValueAtIndex(i), aValue, sizeof(nsCSSValue)); + } + + void SetPropertyAtIndex(uint32_t i, nsCSSPropertyID aProperty) { + MOZ_ASSERT(i < mNumProps, "set property index out of range"); + CompressedProperties()[i] = (CompressedCSSProperty)aProperty; + } + + void SetNumPropsToZero() { + mNumProps = 0; + } +}; + +// Make sure the values and properties are aligned appropriately. (These +// assertions are stronger than necessary to keep them simple.) +static_assert(sizeof(nsCSSCompressedDataBlock) == 8, + "nsCSSCompressedDataBlock's size has changed"); +static_assert(NS_ALIGNMENT_OF(nsCSSValue) == 4 || NS_ALIGNMENT_OF(nsCSSValue) == 8, + "nsCSSValue doesn't align with nsCSSCompressedDataBlock"); +static_assert(NS_ALIGNMENT_OF(nsCSSCompressedDataBlock::CompressedCSSProperty) == 2, + "CompressedCSSProperty doesn't align with nsCSSValue"); + +// Make sure that sizeof(CompressedCSSProperty) is big enough. +static_assert(eCSSProperty_COUNT_no_shorthands <= + nsCSSCompressedDataBlock::MaxCompressedCSSProperty, + "nsCSSPropertyID doesn't fit in StoredSizeOfCSSProperty"); + +class nsCSSExpandedDataBlock +{ + friend class nsCSSCompressedDataBlock; + +public: + nsCSSExpandedDataBlock(); + ~nsCSSExpandedDataBlock(); + +private: + /* Property storage may not be accessed directly; use AddLonghandProperty + * and friends. + */ + nsCSSValue mValues[eCSSProperty_COUNT_no_shorthands]; + +public: + /** + * Transfer all of the state from a pair of compressed data blocks + * to this expanded block. This expanded block must be clear + * beforehand. + * + * This method DELETES both of the compressed data blocks it is + * passed. (This is necessary because ownership of sub-objects + * is transferred to the expanded block.) + */ + void Expand(nsCSSCompressedDataBlock *aNormalBlock, + nsCSSCompressedDataBlock *aImportantBlock); + + /** + * Allocate new compressed blocks and transfer all of the state + * from this expanded block to the new blocks, clearing this + * expanded block. A normal block will always be allocated, but + * an important block will only be allocated if there are + * !important properties in the expanded block; otherwise + * |*aImportantBlock| will be set to null. + * + * aOrder is an array of nsCSSPropertyID values specifying the order + * to store values in the two data blocks. + */ + void Compress(nsCSSCompressedDataBlock **aNormalBlock, + nsCSSCompressedDataBlock **aImportantBlock, + const nsTArray& aOrder); + + /** + * Copy a value into this expanded block. This does NOT destroy + * the source value object. |aProperty| cannot be a shorthand. + */ + void AddLonghandProperty(nsCSSPropertyID aProperty, const nsCSSValue& aValue); + + /** + * Clear the state of this expanded block. + */ + void Clear(); + + /** + * Clear the data for the given property (including the set and + * important bits). Can be used with shorthand properties. + */ + void ClearProperty(nsCSSPropertyID aPropID); + + /** + * Same as ClearProperty, but faster and cannot be used with shorthands. + */ + void ClearLonghandProperty(nsCSSPropertyID aPropID); + + /** + * Transfer the state for |aPropID| (which may be a shorthand) + * from |aFromBlock| to this block. The property being transferred + * is !important if |aIsImportant| is true, and should replace an + * existing !important property regardless of its own importance + * if |aOverrideImportant| is true. |aEnabledState| is used to + * determine which longhand components of |aPropID| (if it is a + * shorthand) to transfer. + * + * Returns true if something changed, false otherwise. Calls + * |ValueAppended| on |aDeclaration| if the property was not + * previously set, or in any case if |aMustCallValueAppended| is true. + * Calls |SetDocumentAndPageUseCounter| on |aSheetDocument| if it is + * non-null and |aPropID| has a use counter. + */ + bool TransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, + nsCSSPropertyID aPropID, + mozilla::CSSEnabledState aEnabledState, + bool aIsImportant, + bool aOverrideImportant, + bool aMustCallValueAppended, + mozilla::css::Declaration* aDeclaration, + nsIDocument* aSheetDocument); + + /** + * Copies the values for aPropID into the specified aRuleData object. + * + * This is used for copying parsed-at-computed-value-time properties + * that had variable references. aPropID must be a longhand property. + */ + void MapRuleInfoInto(nsCSSPropertyID aPropID, nsRuleData* aRuleData) const; + + void AssertInitialState() { +#ifdef DEBUG + DoAssertInitialState(); +#endif + } + +private: + /** + * Compute the number of properties that will be present in the + * result of |Compress|. + */ + void ComputeNumProps(uint32_t* aNumPropsNormal, + uint32_t* aNumPropsImportant); + + void DoExpand(nsCSSCompressedDataBlock *aBlock, bool aImportant); + + /** + * Worker for TransferFromBlock; cannot be used with shorthands. + */ + bool DoTransferFromBlock(nsCSSExpandedDataBlock& aFromBlock, + nsCSSPropertyID aPropID, + bool aIsImportant, + bool aOverrideImportant, + bool aMustCallValueAppended, + mozilla::css::Declaration* aDeclaration, + nsIDocument* aSheetDocument); + +#ifdef DEBUG + void DoAssertInitialState(); +#endif + + /* + * mPropertiesSet stores a bit for every property that is present, + * to optimize compression of blocks with small numbers of + * properties (the norm) and to allow quickly checking whether a + * property is set in this block. + */ + nsCSSPropertyIDSet mPropertiesSet; + /* + * mPropertiesImportant indicates which properties are '!important'. + */ + nsCSSPropertyIDSet mPropertiesImportant; + + /* + * Return the storage location within |this| of the value of the + * property |aProperty|. + */ + nsCSSValue* PropertyAt(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && + aProperty < eCSSProperty_COUNT_no_shorthands, + "property out of range"); + return &mValues[aProperty]; + } + const nsCSSValue* PropertyAt(nsCSSPropertyID aProperty) const { + MOZ_ASSERT(0 <= aProperty && + aProperty < eCSSProperty_COUNT_no_shorthands, + "property out of range"); + return &mValues[aProperty]; + } + + void SetPropertyBit(nsCSSPropertyID aProperty) { + mPropertiesSet.AddProperty(aProperty); + } + + void ClearPropertyBit(nsCSSPropertyID aProperty) { + mPropertiesSet.RemoveProperty(aProperty); + } + + bool HasPropertyBit(nsCSSPropertyID aProperty) { + return mPropertiesSet.HasProperty(aProperty); + } + + void SetImportantBit(nsCSSPropertyID aProperty) { + mPropertiesImportant.AddProperty(aProperty); + } + + void ClearImportantBit(nsCSSPropertyID aProperty) { + mPropertiesImportant.RemoveProperty(aProperty); + } + + bool HasImportantBit(nsCSSPropertyID aProperty) { + return mPropertiesImportant.HasProperty(aProperty); + } + + void ClearSets() { + mPropertiesSet.Empty(); + mPropertiesImportant.Empty(); + } +}; + +#endif /* !defined(nsCSSDataBlock_h__) */ diff --git a/layout/style/nsCSSFontDescList.h b/layout/style/nsCSSFontDescList.h new file mode 100644 index 000000000..0c614c9b2 --- /dev/null +++ b/layout/style/nsCSSFontDescList.h @@ -0,0 +1,14 @@ +/* -*- 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/. */ + +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-language-override, FontLanguageOverride) +CSS_FONT_DESC(font-display, Display) diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h new file mode 100644 index 000000000..febdd32c6 --- /dev/null +++ b/layout/style/nsCSSKeywordList.h @@ -0,0 +1,799 @@ +/* -*- 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/. */ + +/* keywords used within CSS property values */ + +/****** + + This file contains the list of all parsed CSS keywords + See nsCSSKeywords.h for access to the enum values for keywords + + It is designed to be used as inline input to nsCSSKeywords.cpp *only* + through the magic of C preprocessing. + + All entries must be enclosed in the macro CSS_KEY which will have cruel + and unusual things done to it + + It is recommended (but not strictly necessary) to keep all entries + in alphabetical order + + Requirements: + + Entries are in the form: (name, id). 'id' must always be the same as 'name' + except that all hyphens ('-') in 'name' are converted to underscores ('_') + in 'id'. This lets us do nice things with the macros without having to + copy/convert strings at runtime. + + 'name' entries *must* use only lowercase characters. + + ** Break these invariants and bad things will happen. ** + + ******/ + +// OUTPUT_CLASS=nsCSSKeywords +// MACRO_NAME=CSS_KEY + +CSS_KEY(-moz-activehyperlinktext, _moz_activehyperlinktext) +CSS_KEY(-moz-all, _moz_all) +CSS_KEY(-moz-alt-content, _moz_alt_content) +CSS_KEY(-moz-anchor-decoration, _moz_anchor_decoration) +CSS_KEY(-moz-available, _moz_available) +CSS_KEY(-moz-box, _moz_box) +CSS_KEY(-moz-button, _moz_button) +CSS_KEY(-moz-buttondefault, _moz_buttondefault) +CSS_KEY(-moz-buttonhoverface, _moz_buttonhoverface) +CSS_KEY(-moz-buttonhovertext, _moz_buttonhovertext) +CSS_KEY(-moz-cellhighlight, _moz_cellhighlight) +CSS_KEY(-moz-cellhighlighttext, _moz_cellhighlighttext) +CSS_KEY(-moz-center, _moz_center) +CSS_KEY(-moz-combobox, _moz_combobox) +CSS_KEY(-moz-comboboxtext, _moz_comboboxtext) +CSS_KEY(-moz-block-height, _moz_block_height) +CSS_KEY(-moz-deck, _moz_deck) +CSS_KEY(-moz-default-background-color, _moz_default_background_color) +CSS_KEY(-moz-default-color, _moz_default_color) +CSS_KEY(-moz-desktop, _moz_desktop) +CSS_KEY(-moz-dialog, _moz_dialog) +CSS_KEY(-moz-dialogtext, _moz_dialogtext) +CSS_KEY(-moz-document, _moz_document) +CSS_KEY(-moz-dragtargetzone, _moz_dragtargetzone) +CSS_KEY(-moz-element, _moz_element) +CSS_KEY(-moz-eventreerow, _moz_eventreerow) +CSS_KEY(-moz-field, _moz_field) +CSS_KEY(-moz-fieldtext, _moz_fieldtext) +CSS_KEY(-moz-fit-content, _moz_fit_content) +CSS_KEY(-moz-fixed, _moz_fixed) +CSS_KEY(-moz-grabbing, _moz_grabbing) +CSS_KEY(-moz-grab, _moz_grab) +CSS_KEY(-moz-grid-group, _moz_grid_group) +CSS_KEY(-moz-grid-line, _moz_grid_line) +CSS_KEY(-moz-grid, _moz_grid) +CSS_KEY(-moz-groupbox, _moz_groupbox) +CSS_KEY(-moz-gtk-info-bar, _moz_gtk_info_bar) +CSS_KEY(-moz-gtk-info-bar-text, _moz_gtk_info_bar_text) +CSS_KEY(-moz-hidden-unscrollable, _moz_hidden_unscrollable) +CSS_KEY(-moz-hyperlinktext, _moz_hyperlinktext) +CSS_KEY(-moz-html-cellhighlight, _moz_html_cellhighlight) +CSS_KEY(-moz-html-cellhighlighttext, _moz_html_cellhighlighttext) +CSS_KEY(-moz-image-rect, _moz_image_rect) +CSS_KEY(-moz-info, _moz_info) +CSS_KEY(-moz-inline-box, _moz_inline_box) +CSS_KEY(-moz-inline-grid, _moz_inline_grid) +CSS_KEY(-moz-inline-stack, _moz_inline_stack) +CSS_KEY(-moz-isolate, _moz_isolate) +CSS_KEY(-moz-isolate-override, _moz_isolate_override) +CSS_KEY(-moz-left, _moz_left) +CSS_KEY(-moz-list, _moz_list) +CSS_KEY(-moz-mac-buttonactivetext, _moz_mac_buttonactivetext) +CSS_KEY(-moz-mac-chrome-active, _moz_mac_chrome_active) +CSS_KEY(-moz-mac-chrome-inactive, _moz_mac_chrome_inactive) +CSS_KEY(-moz-mac-defaultbuttontext, _moz_mac_defaultbuttontext) +CSS_KEY(-moz-mac-focusring, _moz_mac_focusring) +CSS_KEY(-moz-mac-fullscreen-button, _moz_mac_fullscreen_button) +CSS_KEY(-moz-mac-menuselect, _moz_mac_menuselect) +CSS_KEY(-moz-mac-menushadow, _moz_mac_menushadow) +CSS_KEY(-moz-mac-menutextdisable, _moz_mac_menutextdisable) +CSS_KEY(-moz-mac-menutextselect, _moz_mac_menutextselect) +CSS_KEY(-moz-mac-disabledtoolbartext, _moz_mac_disabledtoolbartext) +CSS_KEY(-moz-mac-secondaryhighlight, _moz_mac_secondaryhighlight) +CSS_KEY(-moz-max-content, _moz_max_content) +CSS_KEY(-moz-menuhover, _moz_menuhover) +CSS_KEY(-moz-menuhovertext, _moz_menuhovertext) +CSS_KEY(-moz-menubartext, _moz_menubartext) +CSS_KEY(-moz-menubarhovertext, _moz_menubarhovertext) +CSS_KEY(-moz-middle-with-baseline, _moz_middle_with_baseline) +CSS_KEY(-moz-min-content, _moz_min_content) +CSS_KEY(-moz-nativehyperlinktext, _moz_nativehyperlinktext) +CSS_KEY(-moz-none, _moz_none) +CSS_KEY(-moz-oddtreerow, _moz_oddtreerow) +CSS_KEY(-moz-plaintext, _moz_plaintext) +CSS_KEY(-moz-popup, _moz_popup) +CSS_KEY(-moz-pre-space, _moz_pre_space) +CSS_KEY(-moz-pull-down-menu, _moz_pull_down_menu) +CSS_KEY(-moz-right, _moz_right) +CSS_KEY(-moz-scrollbars-horizontal, _moz_scrollbars_horizontal) +CSS_KEY(-moz-scrollbars-none, _moz_scrollbars_none) +CSS_KEY(-moz-scrollbars-vertical, _moz_scrollbars_vertical) +CSS_KEY(-moz-stack, _moz_stack) +CSS_KEY(-moz-text, _moz_text) +CSS_KEY(-moz-use-system-font, _moz_use_system_font) +CSS_KEY(-moz-visitedhyperlinktext, _moz_visitedhyperlinktext) +CSS_KEY(-moz-window, _moz_window) +CSS_KEY(-moz-workspace, _moz_workspace) +CSS_KEY(-moz-zoom-in, _moz_zoom_in) +CSS_KEY(-moz-zoom-out, _moz_zoom_out) +CSS_KEY(-webkit-box, _webkit_box) +CSS_KEY(-webkit-flex, _webkit_flex) +CSS_KEY(-webkit-inline-box, _webkit_inline_box) +CSS_KEY(-webkit-inline-flex, _webkit_inline_flex) +CSS_KEY(absolute, absolute) +CSS_KEY(active, active) +CSS_KEY(activeborder, activeborder) +CSS_KEY(activecaption, activecaption) +CSS_KEY(add, add) +CSS_KEY(additive, additive) +CSS_KEY(alias, alias) +CSS_KEY(all, all) +CSS_KEY(all-petite-caps, all_petite_caps) +CSS_KEY(all-scroll, all_scroll) +CSS_KEY(all-small-caps, all_small_caps) +CSS_KEY(alpha, alpha) +CSS_KEY(alternate, alternate) +CSS_KEY(alternate-reverse, alternate_reverse) +CSS_KEY(always, always) +CSS_KEY(annotation, annotation) +CSS_KEY(appworkspace, appworkspace) +CSS_KEY(auto, auto) +CSS_KEY(auto-fill, auto_fill) +CSS_KEY(auto-fit, auto_fit) +CSS_KEY(auto-flow, auto_flow) +CSS_KEY(avoid, avoid) +CSS_KEY(background, background) +CSS_KEY(backwards, backwards) +CSS_KEY(balance, balance) +CSS_KEY(baseline, baseline) +CSS_KEY(bidi-override, bidi_override) +CSS_KEY(blink, blink) +CSS_KEY(block, block) +CSS_KEY(block-axis, block_axis) +CSS_KEY(blur, blur) +CSS_KEY(bold, bold) +CSS_KEY(bold-fraktur, bold_fraktur) +CSS_KEY(bold-italic, bold_italic) +CSS_KEY(bold-sans-serif, bold_sans_serif) +CSS_KEY(bold-script, bold_script) +CSS_KEY(bolder, bolder) +CSS_KEY(border-box, border_box) +CSS_KEY(both, both) +CSS_KEY(bottom, bottom) +CSS_KEY(bottom-outside, bottom_outside) +CSS_KEY(break-all, break_all) +CSS_KEY(break-word, break_word) +CSS_KEY(brightness, brightness) +CSS_KEY(browser, browser) +CSS_KEY(bullets, bullets) +CSS_KEY(button, button) +CSS_KEY(buttonface, buttonface) +CSS_KEY(buttonhighlight, buttonhighlight) +CSS_KEY(buttonshadow, buttonshadow) +CSS_KEY(buttontext, buttontext) +CSS_KEY(capitalize, capitalize) +CSS_KEY(caption, caption) +CSS_KEY(captiontext, captiontext) +CSS_KEY(cell, cell) +CSS_KEY(center, center) +CSS_KEY(ch, ch) +CSS_KEY(character-variant, character_variant) +CSS_KEY(circle, circle) +CSS_KEY(cjk-decimal, cjk_decimal) +CSS_KEY(clip, clip) +CSS_KEY(clone, clone) +CSS_KEY(close-quote, close_quote) +CSS_KEY(closest-corner, closest_corner) +CSS_KEY(closest-side, closest_side) +CSS_KEY(cm, cm) +CSS_KEY(col-resize, col_resize) +CSS_KEY(collapse, collapse) +CSS_KEY(color, color) +CSS_KEY(color-burn, color_burn) +CSS_KEY(color-dodge, color_dodge) +CSS_KEY(common-ligatures, common_ligatures) +CSS_KEY(column, column) +CSS_KEY(column-reverse, column_reverse) +CSS_KEY(condensed, condensed) +CSS_KEY(contain, contain) +CSS_KEY(content-box, content_box) +CSS_KEY(contents, contents) +CSS_KEY(context-fill, context_fill) +CSS_KEY(context-fill-opacity, context_fill_opacity) +CSS_KEY(context-menu, context_menu) +CSS_KEY(context-stroke, context_stroke) +CSS_KEY(context-stroke-opacity, context_stroke_opacity) +CSS_KEY(context-value, context_value) +CSS_KEY(continuous, continuous) +CSS_KEY(contrast, contrast) +CSS_KEY(copy, copy) +CSS_KEY(contextual, contextual) +CSS_KEY(cover, cover) +CSS_KEY(crop, crop) +CSS_KEY(cross, cross) +CSS_KEY(crosshair, crosshair) +CSS_KEY(currentcolor, currentcolor) +CSS_KEY(cursive, cursive) +CSS_KEY(cyclic, cyclic) +CSS_KEY(darken, darken) +CSS_KEY(dashed, dashed) +CSS_KEY(dense, dense) +CSS_KEY(decimal, decimal) +CSS_KEY(default, default) +CSS_KEY(deg, deg) +CSS_KEY(diagonal-fractions, diagonal_fractions) +CSS_KEY(dialog, dialog) +CSS_KEY(difference, difference) +CSS_KEY(digits, digits) +CSS_KEY(disabled, disabled) +CSS_KEY(disc, disc) +CSS_KEY(disclosure-closed, disclosure_closed) +CSS_KEY(disclosure-open, disclosure_open) +CSS_KEY(discretionary-ligatures, discretionary_ligatures) +CSS_KEY(dot, dot) +CSS_KEY(dotted, dotted) +CSS_KEY(double, double) +CSS_KEY(double-circle, double_circle) +CSS_KEY(double-struck, double_struck) +CSS_KEY(drag, drag) +CSS_KEY(drop-shadow, drop_shadow) +CSS_KEY(e-resize, e_resize) +CSS_KEY(ease, ease) +CSS_KEY(ease-in, ease_in) +CSS_KEY(ease-in-out, ease_in_out) +CSS_KEY(ease-out, ease_out) +CSS_KEY(economy, economy) +CSS_KEY(element, element) +CSS_KEY(elements, elements) +CSS_KEY(ellipse, ellipse) +CSS_KEY(ellipsis, ellipsis) +CSS_KEY(em, em) +CSS_KEY(embed, embed) +CSS_KEY(enabled, enabled) +CSS_KEY(end, end) +CSS_KEY(ethiopic-numeric, ethiopic_numeric) +CSS_KEY(ex, ex) +CSS_KEY(exact, exact) +CSS_KEY(exclude, exclude) +CSS_KEY(exclusion, exclusion) +CSS_KEY(expanded, expanded) +CSS_KEY(extends, extends) +CSS_KEY(extra-condensed, extra_condensed) +CSS_KEY(extra-expanded, extra_expanded) +CSS_KEY(ew-resize, ew_resize) +CSS_KEY(fallback, fallback) +CSS_KEY(fantasy, fantasy) +CSS_KEY(farthest-side, farthest_side) +CSS_KEY(farthest-corner, farthest_corner) +CSS_KEY(fill, fill) +CSS_KEY(filled, filled) +CSS_KEY(fill-box, fill_box) +CSS_KEY(first, first) +CSS_KEY(fit-content, fit_content) +CSS_KEY(fixed, fixed) +CSS_KEY(flat, flat) +CSS_KEY(flex, flex) +CSS_KEY(flex-end, flex_end) +CSS_KEY(flex-start, flex_start) +CSS_KEY(flip, flip) +CSS_KEY(forwards, forwards) +CSS_KEY(fraktur, fraktur) +CSS_KEY(from-image, from_image) +CSS_KEY(full-width, full_width) +CSS_KEY(fullscreen, fullscreen) +CSS_KEY(grab, grab) +CSS_KEY(grabbing, grabbing) +CSS_KEY(grad, grad) +CSS_KEY(grayscale, grayscale) +CSS_KEY(graytext, graytext) +CSS_KEY(grid, grid) +CSS_KEY(groove, groove) +CSS_KEY(hard-light, hard_light) +CSS_KEY(hebrew, hebrew) +CSS_KEY(help, help) +CSS_KEY(hidden, hidden) +CSS_KEY(hide, hide) +CSS_KEY(highlight, highlight) +CSS_KEY(highlighttext, highlighttext) +CSS_KEY(historical-forms, historical_forms) +CSS_KEY(historical-ligatures, historical_ligatures) +CSS_KEY(horizontal, horizontal) +CSS_KEY(horizontal-tb, horizontal_tb) +CSS_KEY(hue, hue) +CSS_KEY(hue-rotate, hue_rotate) +CSS_KEY(hz, hz) +CSS_KEY(icon, icon) +CSS_KEY(ignore, ignore) +CSS_KEY(in, in) +CSS_KEY(interlace, interlace) +CSS_KEY(inactive, inactive) +CSS_KEY(inactiveborder, inactiveborder) +CSS_KEY(inactivecaption, inactivecaption) +CSS_KEY(inactivecaptiontext, inactivecaptiontext) +CSS_KEY(infinite, infinite) +CSS_KEY(infobackground, infobackground) +CSS_KEY(infotext, infotext) +CSS_KEY(inherit, inherit) +CSS_KEY(initial, initial) +CSS_KEY(inline, inline) +CSS_KEY(inline-axis, inline_axis) +CSS_KEY(inline-block, inline_block) +CSS_KEY(inline-end, inline_end) +CSS_KEY(inline-flex, inline_flex) +CSS_KEY(inline-grid, inline_grid) +CSS_KEY(inline-start, inline_start) +CSS_KEY(inline-table, inline_table) +CSS_KEY(inset, inset) +CSS_KEY(inside, inside) +// CSS_KEY(inter-character, inter_character) // TODO see bug 1055672 +CSS_KEY(interpolatematrix, interpolatematrix) +CSS_KEY(intersect, intersect) +CSS_KEY(isolate, isolate) +CSS_KEY(isolate-override, isolate_override) +CSS_KEY(invert, invert) +CSS_KEY(italic, italic) +CSS_KEY(japanese-formal, japanese_formal) +CSS_KEY(japanese-informal, japanese_informal) +CSS_KEY(jis78, jis78) +CSS_KEY(jis83, jis83) +CSS_KEY(jis90, jis90) +CSS_KEY(jis04, jis04) +CSS_KEY(justify, justify) +CSS_KEY(keep-all, keep_all) +CSS_KEY(khz, khz) +CSS_KEY(korean-hangul-formal, korean_hangul_formal) +CSS_KEY(korean-hanja-formal, korean_hanja_formal) +CSS_KEY(korean-hanja-informal, korean_hanja_informal) +CSS_KEY(landscape, landscape) +CSS_KEY(large, large) +CSS_KEY(larger, larger) +CSS_KEY(last, last) +CSS_KEY(last baseline, last_baseline) // only used for DevTools auto-completion +CSS_KEY(layout, layout) +CSS_KEY(left, left) +CSS_KEY(legacy, legacy) +CSS_KEY(lighten, lighten) +CSS_KEY(lighter, lighter) +CSS_KEY(line-through, line_through) +CSS_KEY(linear, linear) +CSS_KEY(lining-nums, lining_nums) +CSS_KEY(list-item, list_item) +CSS_KEY(local, local) +CSS_KEY(logical, logical) +CSS_KEY(looped, looped) +CSS_KEY(lowercase, lowercase) +CSS_KEY(lr, lr) +CSS_KEY(lr-tb, lr_tb) +CSS_KEY(ltr, ltr) +CSS_KEY(luminance, luminance) +CSS_KEY(luminosity, luminosity) +CSS_KEY(mandatory, mandatory) +CSS_KEY(manipulation, manipulation) +CSS_KEY(manual, manual) +CSS_KEY(margin-box, margin_box) +CSS_KEY(markers, markers) +CSS_KEY(match-parent, match_parent) +CSS_KEY(match-source, match_source) +CSS_KEY(matrix, matrix) +CSS_KEY(matrix3d, matrix3d) +CSS_KEY(max-content, max_content) +CSS_KEY(medium, medium) +CSS_KEY(menu, menu) +CSS_KEY(menutext, menutext) +CSS_KEY(message-box, message_box) +CSS_KEY(middle, middle) +CSS_KEY(min-content, min_content) +CSS_KEY(minmax, minmax) +CSS_KEY(mix, mix) +CSS_KEY(mixed, mixed) +CSS_KEY(mm, mm) +CSS_KEY(monospace, monospace) +CSS_KEY(move, move) +CSS_KEY(ms, ms) +CSS_KEY(multiply, multiply) +CSS_KEY(n-resize, n_resize) +CSS_KEY(narrower, narrower) +CSS_KEY(ne-resize, ne_resize) +CSS_KEY(nesw-resize, nesw_resize) +CSS_KEY(no-close-quote, no_close_quote) +CSS_KEY(no-common-ligatures, no_common_ligatures) +CSS_KEY(no-contextual, no_contextual) +CSS_KEY(no-discretionary-ligatures, no_discretionary_ligatures) +CSS_KEY(no-drag, no_drag) +CSS_KEY(no-drop, no_drop) +CSS_KEY(no-historical-ligatures, no_historical_ligatures) +CSS_KEY(no-open-quote, no_open_quote) +CSS_KEY(no-repeat, no_repeat) +CSS_KEY(none, none) +CSS_KEY(normal, normal) +CSS_KEY(not-allowed, not_allowed) +CSS_KEY(nowrap, nowrap) +CSS_KEY(numeric, numeric) +CSS_KEY(ns-resize, ns_resize) +CSS_KEY(nw-resize, nw_resize) +CSS_KEY(nwse-resize, nwse_resize) +CSS_KEY(oblique, oblique) +CSS_KEY(oldstyle-nums, oldstyle_nums) +CSS_KEY(opacity, opacity) +CSS_KEY(open, open) +CSS_KEY(open-quote, open_quote) +CSS_KEY(optional, optional) +CSS_KEY(ordinal, ordinal) +CSS_KEY(ornaments, ornaments) +CSS_KEY(outset, outset) +CSS_KEY(outside, outside) +CSS_KEY(over, over) +CSS_KEY(overlay, overlay) +CSS_KEY(overline, overline) +CSS_KEY(paint, paint) +CSS_KEY(padding-box, padding_box) +CSS_KEY(painted, painted) +CSS_KEY(pan-x, pan_x) +CSS_KEY(pan-y, pan_y) +CSS_KEY(paused, paused) +CSS_KEY(pc, pc) +CSS_KEY(perspective, perspective) +CSS_KEY(petite-caps, petite_caps) +CSS_KEY(physical, physical) +CSS_KEY(plaintext, plaintext) +CSS_KEY(pointer, pointer) +CSS_KEY(polygon, polygon) +CSS_KEY(portrait, portrait) +CSS_KEY(pre, pre) +CSS_KEY(pre-wrap, pre_wrap) +CSS_KEY(pre-line, pre_line) +CSS_KEY(preserve-3d, preserve_3d) +CSS_KEY(progress, progress) +CSS_KEY(progressive, progressive) +CSS_KEY(proportional-nums, proportional_nums) +CSS_KEY(proportional-width, proportional_width) +CSS_KEY(proximity, proximity) +CSS_KEY(pt, pt) +CSS_KEY(px, px) +CSS_KEY(rad, rad) +CSS_KEY(read-only, read_only) +CSS_KEY(read-write, read_write) +CSS_KEY(relative, relative) +CSS_KEY(repeat, repeat) +CSS_KEY(repeat-x, repeat_x) +CSS_KEY(repeat-y, repeat_y) +CSS_KEY(reverse, reverse) +CSS_KEY(ridge, ridge) +CSS_KEY(right, right) +CSS_KEY(rl, rl) +CSS_KEY(rl-tb, rl_tb) +CSS_KEY(rotate, rotate) +CSS_KEY(rotate3d, rotate3d) +CSS_KEY(rotatex, rotatex) +CSS_KEY(rotatey, rotatey) +CSS_KEY(rotatez, rotatez) +CSS_KEY(round, round) +CSS_KEY(row, row) +CSS_KEY(row-resize, row_resize) +CSS_KEY(row-reverse, row_reverse) +CSS_KEY(rtl, rtl) +CSS_KEY(ruby, ruby) +CSS_KEY(ruby-base, ruby_base) +CSS_KEY(ruby-base-container, ruby_base_container) +CSS_KEY(ruby-text, ruby_text) +CSS_KEY(ruby-text-container, ruby_text_container) +CSS_KEY(running, running) +CSS_KEY(s, s) +CSS_KEY(s-resize, s_resize) +CSS_KEY(safe, safe) +CSS_KEY(saturate, saturate) +CSS_KEY(saturation, saturation) +CSS_KEY(scale, scale) +CSS_KEY(scale-down, scale_down) +CSS_KEY(scale3d, scale3d) +CSS_KEY(scalex, scalex) +CSS_KEY(scaley, scaley) +CSS_KEY(scalez, scalez) +CSS_KEY(screen, screen) +CSS_KEY(script, script) +CSS_KEY(scroll, scroll) +CSS_KEY(scrollbar, scrollbar) +CSS_KEY(scrollbar-small, scrollbar_small) +CSS_KEY(scrollbar-horizontal, scrollbar_horizontal) +CSS_KEY(scrollbar-vertical, scrollbar_vertical) +CSS_KEY(se-resize, se_resize) +CSS_KEY(select-after, select_after) +CSS_KEY(select-all, select_all) +CSS_KEY(select-before, select_before) +CSS_KEY(select-menu, select_menu) +CSS_KEY(select-same, select_same) +CSS_KEY(self-end, self_end) +CSS_KEY(self-start, self_start) +CSS_KEY(semi-condensed, semi_condensed) +CSS_KEY(semi-expanded, semi_expanded) +CSS_KEY(separate, separate) +CSS_KEY(sepia, sepia) +CSS_KEY(serif, serif) +CSS_KEY(sesame, sesame) +CSS_KEY(show, show) +CSS_KEY(sideways, sideways) +CSS_KEY(sideways-lr, sideways_lr) +CSS_KEY(sideways-right, sideways_right) /* alias for 'sideways' */ +CSS_KEY(sideways-rl, sideways_rl) +CSS_KEY(simp-chinese-formal, simp_chinese_formal) +CSS_KEY(simp-chinese-informal, simp_chinese_informal) +CSS_KEY(simplified, simplified) +CSS_KEY(skew, skew) +CSS_KEY(skewx, skewx) +CSS_KEY(skewy, skewy) +CSS_KEY(slashed-zero, slashed_zero) +CSS_KEY(slice, slice) +CSS_KEY(small, small) +CSS_KEY(small-caps, small_caps) +CSS_KEY(small-caption, small_caption) +CSS_KEY(smaller, smaller) +CSS_KEY(smooth, smooth) +CSS_KEY(soft, soft) +CSS_KEY(soft-light, soft_light) +CSS_KEY(solid, solid) +CSS_KEY(space-around, space_around) +CSS_KEY(space-between, space_between) +CSS_KEY(space-evenly, space_evenly) +CSS_KEY(span, span) +CSS_KEY(spell-out, spell_out) +CSS_KEY(square, square) +CSS_KEY(stacked-fractions, stacked_fractions) +CSS_KEY(start, start) +CSS_KEY(static, static) +CSS_KEY(standalone, standalone) +CSS_KEY(status-bar, status_bar) +CSS_KEY(step-end, step_end) +CSS_KEY(step-start, step_start) +CSS_KEY(sticky, sticky) +CSS_KEY(stretch, stretch) +CSS_KEY(stretch-to-fit, stretch_to_fit) +CSS_KEY(stretched, stretched) +CSS_KEY(strict, strict) +CSS_KEY(stroke, stroke) +CSS_KEY(stroke-box, stroke_box) +CSS_KEY(style, style) +CSS_KEY(styleset, styleset) +CSS_KEY(stylistic, stylistic) +CSS_KEY(sub, sub) +CSS_KEY(subgrid, subgrid) +CSS_KEY(subtract, subtract) +CSS_KEY(super, super) +CSS_KEY(sw-resize, sw_resize) +CSS_KEY(swash, swash) +CSS_KEY(swap, swap) +CSS_KEY(table, table) +CSS_KEY(table-caption, table_caption) +CSS_KEY(table-cell, table_cell) +CSS_KEY(table-column, table_column) +CSS_KEY(table-column-group, table_column_group) +CSS_KEY(table-footer-group, table_footer_group) +CSS_KEY(table-header-group, table_header_group) +CSS_KEY(table-row, table_row) +CSS_KEY(table-row-group, table_row_group) +CSS_KEY(tabular-nums, tabular_nums) +CSS_KEY(tailed, tailed) +CSS_KEY(tb, tb) +CSS_KEY(tb-rl, tb_rl) +CSS_KEY(text, text) +CSS_KEY(text-bottom, text_bottom) +CSS_KEY(text-top, text_top) +CSS_KEY(thick, thick) +CSS_KEY(thin, thin) +CSS_KEY(threeddarkshadow, threeddarkshadow) +CSS_KEY(threedface, threedface) +CSS_KEY(threedhighlight, threedhighlight) +CSS_KEY(threedlightshadow, threedlightshadow) +CSS_KEY(threedshadow, threedshadow) +CSS_KEY(titling-caps, titling_caps) +CSS_KEY(toggle, toggle) +CSS_KEY(top, top) +CSS_KEY(top-outside, top_outside) +CSS_KEY(trad-chinese-formal, trad_chinese_formal) +CSS_KEY(trad-chinese-informal, trad_chinese_informal) +CSS_KEY(traditional, traditional) +CSS_KEY(translate, translate) +CSS_KEY(translate3d, translate3d) +CSS_KEY(translatex, translatex) +CSS_KEY(translatey, translatey) +CSS_KEY(translatez, translatez) +CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only +CSS_KEY(triangle, triangle) +CSS_KEY(tri-state, tri_state) +CSS_KEY(ultra-condensed, ultra_condensed) +CSS_KEY(ultra-expanded, ultra_expanded) +CSS_KEY(under, under) +CSS_KEY(underline, underline) +CSS_KEY(unicase, unicase) +CSS_KEY(unsafe, unsafe) +CSS_KEY(unset, unset) +CSS_KEY(uppercase, uppercase) +CSS_KEY(upright, upright) +CSS_KEY(vertical, vertical) +CSS_KEY(vertical-lr, vertical_lr) +CSS_KEY(vertical-rl, vertical_rl) +CSS_KEY(vertical-text, vertical_text) +CSS_KEY(view-box, view_box) +CSS_KEY(visible, visible) +CSS_KEY(visiblefill, visiblefill) +CSS_KEY(visiblepainted, visiblepainted) +CSS_KEY(visiblestroke, visiblestroke) +CSS_KEY(w-resize, w_resize) +CSS_KEY(wait, wait) +CSS_KEY(wavy, wavy) +CSS_KEY(weight, weight) +CSS_KEY(wider, wider) +CSS_KEY(window, window) +CSS_KEY(windowframe, windowframe) +CSS_KEY(windowtext, windowtext) +CSS_KEY(words, words) +CSS_KEY(wrap, wrap) +CSS_KEY(wrap-reverse, wrap_reverse) +CSS_KEY(write-only, write_only) +CSS_KEY(x-large, x_large) +CSS_KEY(x-small, x_small) +CSS_KEY(xx-large, xx_large) +CSS_KEY(xx-small, xx_small) +CSS_KEY(zoom-in, zoom_in) +CSS_KEY(zoom-out, zoom_out) + +// Appearance keywords for widget styles +CSS_KEY(radio, radio) +CSS_KEY(checkbox, checkbox) +CSS_KEY(button-bevel, button_bevel) +CSS_KEY(toolbox, toolbox) +CSS_KEY(toolbar, toolbar) +CSS_KEY(toolbarbutton, toolbarbutton) +CSS_KEY(toolbargripper, toolbargripper) +CSS_KEY(dualbutton, dualbutton) +CSS_KEY(toolbarbutton-dropdown, toolbarbutton_dropdown) +CSS_KEY(button-arrow-up, button_arrow_up) +CSS_KEY(button-arrow-down, button_arrow_down) +CSS_KEY(button-arrow-next, button_arrow_next) +CSS_KEY(button-arrow-previous, button_arrow_previous) +CSS_KEY(separator, separator) +CSS_KEY(splitter, splitter) +CSS_KEY(statusbar, statusbar) +CSS_KEY(statusbarpanel, statusbarpanel) +CSS_KEY(resizerpanel, resizerpanel) +CSS_KEY(resizer, resizer) +CSS_KEY(listbox, listbox) +CSS_KEY(listitem, listitem) +CSS_KEY(numbers, numbers) +CSS_KEY(number-input, number_input) +CSS_KEY(treeview, treeview) +CSS_KEY(treeitem, treeitem) +CSS_KEY(treetwisty, treetwisty) +CSS_KEY(treetwistyopen, treetwistyopen) +CSS_KEY(treeline, treeline) +CSS_KEY(treeheader, treeheader) +CSS_KEY(treeheadercell, treeheadercell) +CSS_KEY(treeheadersortarrow, treeheadersortarrow) +CSS_KEY(progressbar, progressbar) +CSS_KEY(progressbar-vertical, progressbar_vertical) +CSS_KEY(progresschunk, progresschunk) +CSS_KEY(progresschunk-vertical, progresschunk_vertical) +CSS_KEY(tab, tab) +CSS_KEY(tabpanels, tabpanels) +CSS_KEY(tabpanel, tabpanel) +CSS_KEY(tab-scroll-arrow-back, tab_scroll_arrow_back) +CSS_KEY(tab-scroll-arrow-forward, tab_scroll_arrow_forward) +CSS_KEY(tooltip, tooltip) +CSS_KEY(spinner, spinner) +CSS_KEY(spinner-upbutton, spinner_upbutton) +CSS_KEY(spinner-downbutton, spinner_downbutton) +CSS_KEY(spinner-textfield, spinner_textfield) +CSS_KEY(scrollbarbutton-up, scrollbarbutton_up) +CSS_KEY(scrollbarbutton-down, scrollbarbutton_down) +CSS_KEY(scrollbarbutton-left, scrollbarbutton_left) +CSS_KEY(scrollbarbutton-right, scrollbarbutton_right) +CSS_KEY(scrollbartrack-horizontal, scrollbartrack_horizontal) +CSS_KEY(scrollbartrack-vertical, scrollbartrack_vertical) +CSS_KEY(scrollbarthumb-horizontal, scrollbarthumb_horizontal) +CSS_KEY(scrollbarthumb-vertical, scrollbarthumb_vertical) +CSS_KEY(sheet, sheet) +CSS_KEY(textfield, textfield) +CSS_KEY(textfield-multiline, textfield_multiline) +CSS_KEY(caret, caret) +CSS_KEY(searchfield, searchfield) +CSS_KEY(menubar, menubar) +CSS_KEY(menupopup, menupopup) +CSS_KEY(menuitem, menuitem) +CSS_KEY(checkmenuitem, checkmenuitem) +CSS_KEY(radiomenuitem, radiomenuitem) +CSS_KEY(menucheckbox, menucheckbox) +CSS_KEY(menuradio, menuradio) +CSS_KEY(menuseparator, menuseparator) +CSS_KEY(menuarrow, menuarrow) +CSS_KEY(menuimage, menuimage) +CSS_KEY(menuitemtext, menuitemtext) +CSS_KEY(menulist, menulist) +CSS_KEY(menulist-button, menulist_button) +CSS_KEY(menulist-text, menulist_text) +CSS_KEY(menulist-textfield, menulist_textfield) +CSS_KEY(meterbar, meterbar) +CSS_KEY(meterchunk, meterchunk) +CSS_KEY(minimal-ui, minimal_ui) +CSS_KEY(range, range) +CSS_KEY(range-thumb, range_thumb) +CSS_KEY(sans-serif, sans_serif) +CSS_KEY(sans-serif-bold-italic, sans_serif_bold_italic) +CSS_KEY(sans-serif-italic, sans_serif_italic) +CSS_KEY(scale-horizontal, scale_horizontal) +CSS_KEY(scale-vertical, scale_vertical) +CSS_KEY(scalethumb-horizontal, scalethumb_horizontal) +CSS_KEY(scalethumb-vertical, scalethumb_vertical) +CSS_KEY(scalethumbstart, scalethumbstart) +CSS_KEY(scalethumbend, scalethumbend) +CSS_KEY(scalethumbtick, scalethumbtick) +CSS_KEY(groupbox, groupbox) +CSS_KEY(checkbox-container, checkbox_container) +CSS_KEY(radio-container, radio_container) +CSS_KEY(checkbox-label, checkbox_label) +CSS_KEY(radio-label, radio_label) +CSS_KEY(button-focus, button_focus) +CSS_KEY(-moz-win-media-toolbox, _moz_win_media_toolbox) +CSS_KEY(-moz-win-communications-toolbox, _moz_win_communications_toolbox) +CSS_KEY(-moz-win-browsertabbar-toolbox, _moz_win_browsertabbar_toolbox) +CSS_KEY(-moz-win-mediatext, _moz_win_mediatext) +CSS_KEY(-moz-win-communicationstext, _moz_win_communicationstext) +CSS_KEY(-moz-win-glass, _moz_win_glass) +CSS_KEY(-moz-win-borderless-glass, _moz_win_borderless_glass) +CSS_KEY(-moz-window-titlebar, _moz_window_titlebar) +CSS_KEY(-moz-window-titlebar-maximized, _moz_window_titlebar_maximized) +CSS_KEY(-moz-window-frame-left, _moz_window_frame_left) +CSS_KEY(-moz-window-frame-right, _moz_window_frame_right) +CSS_KEY(-moz-window-frame-bottom, _moz_window_frame_bottom) +CSS_KEY(-moz-window-button-close, _moz_window_button_close) +CSS_KEY(-moz-window-button-minimize, _moz_window_button_minimize) +CSS_KEY(-moz-window-button-maximize, _moz_window_button_maximize) +CSS_KEY(-moz-window-button-restore, _moz_window_button_restore) +CSS_KEY(-moz-window-button-box, _moz_window_button_box) +CSS_KEY(-moz-window-button-box-maximized, _moz_window_button_box_maximized) +CSS_KEY(-moz-mac-help-button, _moz_mac_help_button) +CSS_KEY(-moz-win-exclude-glass, _moz_win_exclude_glass) +CSS_KEY(-moz-mac-vibrancy-light, _moz_mac_vibrancy_light) +CSS_KEY(-moz-mac-vibrancy-dark, _moz_mac_vibrancy_dark) +CSS_KEY(-moz-mac-disclosure-button-closed, _moz_mac_disclosure_button_closed) +CSS_KEY(-moz-mac-disclosure-button-open, _moz_mac_disclosure_button_open) +CSS_KEY(-moz-mac-source-list, _moz_mac_source_list) +CSS_KEY(-moz-mac-source-list-selection, _moz_mac_source_list_selection) +CSS_KEY(-moz-mac-active-source-list-selection, _moz_mac_active_source_list_selection) +CSS_KEY(alphabetic, alphabetic) +CSS_KEY(bevel, bevel) +CSS_KEY(butt, butt) +CSS_KEY(central, central) +CSS_KEY(crispedges, crispedges) +CSS_KEY(evenodd, evenodd) +CSS_KEY(geometricprecision, geometricprecision) +CSS_KEY(hanging, hanging) +CSS_KEY(ideographic, ideographic) +CSS_KEY(linearrgb, linearrgb) +CSS_KEY(mathematical, mathematical) +//CSS_KEY(middle, middle) +CSS_KEY(miter, miter) +CSS_KEY(no-change, no_change) +CSS_KEY(non-scaling-stroke, non_scaling_stroke) +CSS_KEY(nonzero, nonzero) +CSS_KEY(optimizelegibility, optimizelegibility) +CSS_KEY(optimizequality, optimizequality) +CSS_KEY(optimizespeed, optimizespeed) +CSS_KEY(reset-size, reset_size) +//CSS_KEY(square, square) +//CSS_KEY(start, start) +CSS_KEY(srgb, srgb) +CSS_KEY(symbolic, symbolic) +CSS_KEY(symbols, symbols) +CSS_KEY(text-after-edge, text_after_edge) +CSS_KEY(text-before-edge, text_before_edge) +CSS_KEY(use-script, use_script) +CSS_KEY(-moz-crisp-edges, _moz_crisp_edges) +CSS_KEY(space, space) + diff --git a/layout/style/nsCSSKeywords.cpp b/layout/style/nsCSSKeywords.cpp new file mode 100644 index 000000000..8dd362e7c --- /dev/null +++ b/layout/style/nsCSSKeywords.cpp @@ -0,0 +1,87 @@ +/* -*- 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/. */ + +/* keywords used within CSS property values */ + +#include "nsCSSKeywords.h" +#include "nsString.h" +#include "nsStaticNameTable.h" + +// required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it +extern const char* const kCSSRawKeywords[]; + +// define an array of all CSS keywords +#define CSS_KEY(_name,_id) #_name, +const char* const kCSSRawKeywords[] = { +#include "nsCSSKeywordList.h" +}; +#undef CSS_KEY + +static int32_t gKeywordTableRefCount; +static nsStaticCaseInsensitiveNameTable* gKeywordTable; + +void +nsCSSKeywords::AddRefTable(void) +{ + if (0 == gKeywordTableRefCount++) { + NS_ASSERTION(!gKeywordTable, "pre existing array!"); + gKeywordTable = + new nsStaticCaseInsensitiveNameTable(kCSSRawKeywords, eCSSKeyword_COUNT); +#ifdef DEBUG + // Partially verify the entries. + int32_t index = 0; + for (; index < eCSSKeyword_COUNT && kCSSRawKeywords[index]; ++index) { + nsAutoCString temp(kCSSRawKeywords[index]); + NS_ASSERTION(-1 == temp.FindChar('_'), "underscore char in table"); + } + NS_ASSERTION(index == eCSSKeyword_COUNT, "kCSSRawKeywords and eCSSKeyword_COUNT are out of sync"); +#endif + } +} + +void +nsCSSKeywords::ReleaseTable(void) +{ + if (0 == --gKeywordTableRefCount) { + if (gKeywordTable) { + delete gKeywordTable; + gKeywordTable = nullptr; + } + } +} + +nsCSSKeyword +nsCSSKeywords::LookupKeyword(const nsACString& aKeyword) +{ + NS_ASSERTION(gKeywordTable, "no lookup table, needs addref"); + if (gKeywordTable) { + return nsCSSKeyword(gKeywordTable->Lookup(aKeyword)); + } + return eCSSKeyword_UNKNOWN; +} + +nsCSSKeyword +nsCSSKeywords::LookupKeyword(const nsAString& aKeyword) +{ + NS_ASSERTION(gKeywordTable, "no lookup table, needs addref"); + if (gKeywordTable) { + return nsCSSKeyword(gKeywordTable->Lookup(aKeyword)); + } + return eCSSKeyword_UNKNOWN; +} + +const nsAFlatCString& +nsCSSKeywords::GetStringValue(nsCSSKeyword aKeyword) +{ + NS_ASSERTION(gKeywordTable, "no lookup table, needs addref"); + NS_ASSERTION(0 <= aKeyword && aKeyword < eCSSKeyword_COUNT, "out of range"); + if (gKeywordTable) { + return gKeywordTable->GetStringValue(int32_t(aKeyword)); + } else { + static nsDependentCString kNullStr(""); + return kNullStr; + } +} + diff --git a/layout/style/nsCSSKeywords.h b/layout/style/nsCSSKeywords.h new file mode 100644 index 000000000..9e7aeadba --- /dev/null +++ b/layout/style/nsCSSKeywords.h @@ -0,0 +1,42 @@ +/* -*- 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/. */ + +/* keywords used within CSS property values */ + +#ifndef nsCSSKeywords_h___ +#define nsCSSKeywords_h___ + +#include "nsStringFwd.h" + +/* + Declare the enum list using the magic of preprocessing + enum values are "eCSSKeyword_foo" (where foo is the keyword) + + To change the list of keywords, see nsCSSKeywordList.h + + */ +#define CSS_KEY(_name,_id) eCSSKeyword_##_id, +enum nsCSSKeyword : int16_t { + eCSSKeyword_UNKNOWN = -1, +#include "nsCSSKeywordList.h" + eCSSKeyword_COUNT +}; +#undef CSS_KEY + + +class nsCSSKeywords { +public: + static void AddRefTable(void); + static void ReleaseTable(void); + + // Given a keyword string, return the enum value + static nsCSSKeyword LookupKeyword(const nsACString& aKeyword); + static nsCSSKeyword LookupKeyword(const nsAString& aKeyword); + + // Given a keyword enum, get the string value + static const nsAFlatCString& GetStringValue(nsCSSKeyword aKeyword); +}; + +#endif /* nsCSSKeywords_h___ */ diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp new file mode 100644 index 000000000..1108ce5b5 --- /dev/null +++ b/layout/style/nsCSSParser.cpp @@ -0,0 +1,18324 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 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/. */ + +/* parsing of CSS stylesheets, based on a token stream from the CSS scanner */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Move.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/TypedEnumBits.h" + +#include // for std::stable_sort +#include // for std::numeric_limits + +#include "nsCSSParser.h" +#include "nsAlgorithm.h" +#include "nsCSSProps.h" +#include "nsCSSKeywords.h" +#include "nsCSSScanner.h" +#include "mozilla/css/ErrorReporter.h" +#include "mozilla/css/Loader.h" +#include "mozilla/css/StyleRule.h" +#include "mozilla/css/ImportRule.h" +#include "nsCSSRules.h" +#include "mozilla/css/NameSpaceRule.h" +#include "nsTArray.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Declaration.h" +#include "nsStyleConsts.h" +#include "nsNetUtil.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIAtom.h" +#include "nsColor.h" +#include "nsCSSPseudoClasses.h" +#include "nsCSSPseudoElements.h" +#include "nsCSSAnonBoxes.h" +#include "nsNameSpaceManager.h" +#include "nsXMLNameSpaceMap.h" +#include "nsError.h" +#include "nsIMediaList.h" +#include "nsStyleUtil.h" +#include "nsIPrincipal.h" +#include "nsICSSUnprefixingService.h" +#include "mozilla/Sprintf.h" +#include "nsContentUtils.h" +#include "nsAutoPtr.h" +#include "CSSCalc.h" +#include "nsMediaFeatures.h" +#include "nsLayoutUtils.h" +#include "mozilla/Preferences.h" +#include "nsRuleData.h" +#include "mozilla/CSSVariableValues.h" +#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" +#include "mozilla/dom/URL.h" +#include "gfxFontFamilyList.h" + +using namespace mozilla; +using namespace mozilla::css; + +typedef nsCSSProps::KTableEntry KTableEntry; + +// pref-backed bool values (hooked up in nsCSSParser::Startup) +static bool sOpentypeSVGEnabled; +static bool sWebkitPrefixedAliasesEnabled; +static bool sWebkitDevicePixelRatioEnabled; +static bool sUnprefixingServiceEnabled; +#ifdef NIGHTLY_BUILD +static bool sUnprefixingServiceGloballyWhitelisted; +#endif +static bool sMozGradientsEnabled; +static bool sControlCharVisibility; + +const uint32_t +nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = { +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + parsevariant_, +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +}; + +// Maximum number of repetitions for the repeat() function +// in the grid-template-rows and grid-template-columns properties, +// to limit high memory usage from small stylesheets. +// Must be a positive integer. Should be large-ish. +#define GRID_TEMPLATE_MAX_REPETITIONS 10000 + +// End-of-array marker for mask arguments to ParseBitmaskValues +#define MASK_END_VALUE (-1) + +enum class CSSParseResult : int32_t { + // Parsed something successfully: + Ok, + // Did not find what we were looking for, but did not consume any token: + NotFound, + // Unexpected token or token value, too late for UngetToken() to be enough: + Error +}; + +enum class GridTrackSizeFlags { + eDefaultTrackSize = 0x0, + eFixedTrackSize = 0x1, // parse a instead of +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackSizeFlags) + +enum class GridTrackListFlags { + eDefaultTrackList = 0x0, // parse a + eExplicitTrackList = 0x1, // parse an instead +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackListFlags) + +namespace { + +// Rule processing function +typedef void (* RuleAppendFunc) (css::Rule* aRule, void* aData); +static void AssignRuleToPointer(css::Rule* aRule, void* aPointer); +static void AppendRuleToSheet(css::Rule* aRule, void* aParser); + +struct CSSParserInputState { + nsCSSScannerPosition mPosition; + nsCSSToken mToken; + bool mHavePushBack; +}; + +static_assert(css::eAuthorSheetFeatures == 0 && + css::eUserSheetFeatures == 1 && + css::eAgentSheetFeatures == 2, + "sheet parsing mode constants won't fit " + "in CSSParserImpl::mParsingMode"); + +// Your basic top-down recursive descent style parser +// The exposed methods and members of this class are precisely those +// needed by nsCSSParser, far below. +class CSSParserImpl { +public: + CSSParserImpl(); + ~CSSParserImpl(); + + nsresult SetStyleSheet(CSSStyleSheet* aSheet); + + nsIDocument* GetDocument(); + + nsresult SetQuirkMode(bool aQuirkMode); + + nsresult SetChildLoader(mozilla::css::Loader* aChildLoader); + + // Clears everything set by the above Set*() functions. + void Reset(); + + nsresult ParseSheet(const nsAString& aInput, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + uint32_t aLineNumber, + css::LoaderReusableStyleSheets* aReusableSheets); + + already_AddRefed + ParseStyleAttribute(const nsAString& aAttributeValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aNodePrincipal); + + nsresult ParseDeclarations(const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged); + + nsresult ParseRule(const nsAString& aRule, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + css::Rule** aResult); + + void ParseProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant, + bool aIsSVGMode); + void ParseLonghandProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); + + bool ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult); + + void ParseMediaList(const nsSubstring& aBuffer, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + nsMediaList* aMediaList, + bool aHTMLMode); + + bool ParseSourceSizeList(const nsAString& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + InfallibleTArray< nsAutoPtr >& aQueries, + InfallibleTArray& aValues, + bool aHTMLMode); + + void ParseVariable(const nsAString& aVariableName, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant); + + bool ParseFontFamilyListString(const nsSubstring& aBuffer, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue); + + bool ParseColorString(const nsSubstring& aBuffer, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue, + bool aSuppressErrors /* false */); + + bool ParseMarginString(const nsSubstring& aBuffer, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue, + bool aSuppressErrors /* false */); + + nsresult ParseSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSSelectorList **aSelectorList); + + already_AddRefed + ParseKeyframeRule(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber); + + bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURL, // for error reporting + uint32_t aLineNumber, // for error reporting + InfallibleTArray& aSelectorList); + + bool EvaluateSupportsDeclaration(const nsAString& aProperty, + const nsAString& aValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal); + + bool EvaluateSupportsCondition(const nsAString& aCondition, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal); + + bool ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL, + nsAString& aName); + + bool ParseCounterDescriptor(nsCSSCounterDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); + + bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); + + bool IsValueValidForProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue); + + typedef nsCSSParser::VariableEnumFunc VariableEnumFunc; + + /** + * Parses a CSS token stream value and invokes a callback function for each + * variable reference that is encountered. + * + * @param aPropertyValue The CSS token stream value. + * @param aFunc The callback function to invoke; its parameters are the + * variable name found and the aData argument passed in to this function. + * @param aData User data to pass in to the callback. + * @return Whether aPropertyValue could be parsed as a valid CSS token stream + * value (e.g., without syntactic errors in variable references). + */ + bool EnumerateVariableReferences(const nsAString& aPropertyValue, + VariableEnumFunc aFunc, + void* aData); + + /** + * Parses aPropertyValue as a CSS token stream value and resolves any + * variable references using the variables in aVariables. + * + * @param aPropertyValue The CSS token stream value. + * @param aVariables The set of variable values to use when resolving variable + * references. + * @param aResult Out parameter that gets the resolved value. + * @param aFirstToken Out parameter that gets the type of the first token in + * aResult. + * @param aLastToken Out parameter that gets the type of the last token in + * aResult. + * @return Whether aResult could be parsed successfully and variable reference + * substitution succeeded. + */ + bool ResolveVariableValue(const nsAString& aPropertyValue, + const CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken); + + /** + * Parses a string as a CSS token stream value for particular property, + * resolving any variable references. The parsed property value is stored + * in the specified nsRuleData object. If aShorthandPropertyID has a value + * other than eCSSProperty_UNKNOWN, this is the property that will be parsed; + * otherwise, aPropertyID will be parsed. Either way, only aPropertyID, + * a longhand property, will be copied over to the rule data. + * + * If the property cannot be parsed, it will be treated as if 'initial' or + * 'inherit' were specified, for non-inherited and inherited properties + * respectively. + * + * @param aPropertyID The ID of the longhand property whose value is to be + * copied to the rule data. + * @param aShorthandPropertyID The ID of the shorthand property to be parsed. + * If a longhand property is to be parsed, aPropertyID is that property, + * and aShorthandPropertyID must be eCSSProperty_UNKNOWN. + * @param aValue The CSS token stream value. + * @param aVariables The set of variable values to use when resolving variable + * references. + * @param aRuleData The rule data object into which parsed property value for + * aPropertyID will be stored. + */ + void ParsePropertyWithVariableReferences(nsCSSPropertyID aPropertyID, + nsCSSPropertyID aShorthandPropertyID, + const nsAString& aValue, + const CSSVariableValues* aVariables, + nsRuleData* aRuleData, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal, + CSSStyleSheet* aSheet, + uint32_t aLineNumber, + uint32_t aLineOffset); + + bool AgentRulesEnabled() const { + return mParsingMode == css::eAgentSheetFeatures; + } + bool ChromeRulesEnabled() const { + return mIsChrome; + } + bool UserRulesEnabled() const { + return mParsingMode == css::eAgentSheetFeatures || + mParsingMode == css::eUserSheetFeatures; + } + + CSSEnabledState EnabledState() const { + static_assert(int(CSSEnabledState::eForAllContent) == 0, + "CSSEnabledState::eForAllContent should be zero for " + "this bitfield to work"); + CSSEnabledState enabledState = CSSEnabledState::eForAllContent; + if (AgentRulesEnabled()) { + enabledState |= CSSEnabledState::eInUASheets; + } + if (mIsChrome) { + enabledState |= CSSEnabledState::eInChrome; + } + return enabledState; + } + + nsCSSPropertyID LookupEnabledProperty(const nsAString& aProperty) { + return nsCSSProps::LookupProperty(aProperty, EnabledState()); + } + +protected: + class nsAutoParseCompoundProperty; + friend class nsAutoParseCompoundProperty; + + class nsAutoFailingSupportsRule; + friend class nsAutoFailingSupportsRule; + + class nsAutoSuppressErrors; + friend class nsAutoSuppressErrors; + + void AppendRule(css::Rule* aRule); + friend void AppendRuleToSheet(css::Rule*, void*); // calls AppendRule + + /** + * This helper class automatically calls SetParsingCompoundProperty in its + * constructor and takes care of resetting it to false in its destructor. + */ + class nsAutoParseCompoundProperty { + public: + explicit nsAutoParseCompoundProperty(CSSParserImpl* aParser) : mParser(aParser) + { + NS_ASSERTION(!aParser->IsParsingCompoundProperty(), + "already parsing compound property"); + NS_ASSERTION(aParser, "Null parser?"); + aParser->SetParsingCompoundProperty(true); + } + + ~nsAutoParseCompoundProperty() + { + mParser->SetParsingCompoundProperty(false); + } + private: + CSSParserImpl* mParser; + }; + + /** + * This helper class conditionally sets mInFailingSupportsRule to + * true if aCondition = false, and resets it to its original value in its + * destructor. If we are already somewhere within a failing @supports + * rule, passing in aCondition = true does not change mInFailingSupportsRule. + */ + class nsAutoFailingSupportsRule { + public: + nsAutoFailingSupportsRule(CSSParserImpl* aParser, + bool aCondition) + : mParser(aParser), + mOriginalValue(aParser->mInFailingSupportsRule) + { + if (!aCondition) { + mParser->mInFailingSupportsRule = true; + } + } + + ~nsAutoFailingSupportsRule() + { + mParser->mInFailingSupportsRule = mOriginalValue; + } + + private: + CSSParserImpl* mParser; + bool mOriginalValue; + }; + + /** + * Auto class to set aParser->mSuppressErrors to the specified value + * and restore it to its original value later. + */ + class nsAutoSuppressErrors { + public: + explicit nsAutoSuppressErrors(CSSParserImpl* aParser, + bool aSuppressErrors = true) + : mParser(aParser), + mOriginalValue(aParser->mSuppressErrors) + { + mParser->mSuppressErrors = aSuppressErrors; + } + + ~nsAutoSuppressErrors() + { + mParser->mSuppressErrors = mOriginalValue; + } + + private: + CSSParserImpl* mParser; + bool mOriginalValue; + }; + + /** + * RAII class to set aParser->mInSupportsCondition to true and restore it + * to false later. + */ + class MOZ_RAII nsAutoInSupportsCondition + { + public: + explicit nsAutoInSupportsCondition(CSSParserImpl* aParser) + : mParser(aParser) + { + MOZ_ASSERT(!aParser->mInSupportsCondition, + "nsAutoInSupportsCondition is not designed to be used " + "re-entrantly"); + mParser->mInSupportsCondition = true; + } + + ~nsAutoInSupportsCondition() + { + mParser->mInSupportsCondition = false; + } + + private: + CSSParserImpl* const mParser; + }; + + // the caller must hold on to aString until parsing is done + void InitScanner(nsCSSScanner& aScanner, + css::ErrorReporter& aReporter, + nsIURI* aSheetURI, nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal); + void ReleaseScanner(void); + + /** + * This is a RAII class which behaves like an "AutoRestore<>" for our parser + * input state. When instantiated, this class saves the current parser input + * state (in a CSSParserInputState object), and it restores the parser to + * that state when destructed, unless "DoNotRestore()" has been called. + */ + class MOZ_RAII nsAutoCSSParserInputStateRestorer { + public: + explicit nsAutoCSSParserInputStateRestorer(CSSParserImpl* aParser + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mParser(aParser), + mShouldRestore(true) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + mParser->SaveInputState(mSavedState); + } + + void DoNotRestore() + { + mShouldRestore = false; + } + + ~nsAutoCSSParserInputStateRestorer() + { + if (mShouldRestore) { + mParser->RestoreSavedInputState(mSavedState); + } + } + + private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + CSSParserImpl* mParser; + CSSParserInputState mSavedState; + bool mShouldRestore; + }; + + /** + * This is a RAII class which creates a temporary nsCSSScanner for the given + * string, and reconfigures aParser to use *that* scanner instead of its + * existing scanner, until we go out of scope. (This allows us to rewrite + * a portion of a stylesheet using a temporary string, and switch to parsing + * that rewritten section, and then resume parsing the original stylesheet.) + * + * aParser must have a non-null nsCSSScanner (which we'll be temporarily + * replacing) and ErrorReporter (which this class will co-opt for the + * temporary parser). While we're in scope, we also suppress error reporting, + * so it doesn't really matter which reporter we use. We suppress reporting + * because this class is only used with CSS that is synthesized & didn't + * come directly from an author, and it would be confusing if we reported + * syntax errors for CSS that an author didn't provide. + * + * XXXdholbert we could also change this & report errors, if needed. Might + * want to customize the error reporting somehow though. + */ + class MOZ_RAII nsAutoScannerChanger { + public: + nsAutoScannerChanger(CSSParserImpl* aParser, + const nsAString& aStringToScan + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mParser(aParser), + mOriginalScanner(aParser->mScanner), + mStringScanner(aStringToScan, 0), + mParserStateRestorer(aParser), + mErrorSuppresser(aParser) + { + MOZ_ASSERT(mOriginalScanner, + "Shouldn't use nsAutoScannerChanger unless we already " + "have a scanner"); + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + // Set & setup the new scanner: + mParser->mScanner = &mStringScanner; + mStringScanner.SetErrorReporter(mParser->mReporter); + + // We might've had push-back on our original scanner (and if we did, + // that fact is saved via mParserStateRestorer). But we don't have + // push-back in mStringScanner, so clear that flag. + mParser->mHavePushBack = false; + } + + ~nsAutoScannerChanger() + { + // Restore original scanner. All other cleanup is done by RAII members. + mParser->mScanner = mOriginalScanner; + } + + private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER + CSSParserImpl* mParser; + nsCSSScanner *mOriginalScanner; + nsCSSScanner mStringScanner; + nsAutoCSSParserInputStateRestorer mParserStateRestorer; + nsAutoSuppressErrors mErrorSuppresser; + }; + + + bool IsSVGMode() const { + return mScanner->IsSVGMode(); + } + + /** + * Saves the current input state, which includes any currently pushed + * back token, and the current position of the scanner. + */ + void SaveInputState(CSSParserInputState& aState); + + /** + * Restores the saved input state by pushing back any saved pushback + * token and calling RestoreSavedPosition on the scanner. + */ + void RestoreSavedInputState(const CSSParserInputState& aState); + + bool GetToken(bool aSkipWS); + void UngetToken(); + bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum); + void AssertNextTokenAt(uint32_t aLine, uint32_t aCol) + { + // Beware that this method will call GetToken/UngetToken (in + // GetNextTokenLocation) in DEBUG builds, but not in non-DEBUG builds. + DebugOnly lineAfter, colAfter; + MOZ_ASSERT(GetNextTokenLocation(true, &lineAfter, &colAfter) && + lineAfter == aLine && colAfter == aCol, + "shouldn't have consumed any tokens"); + } + + bool ExpectSymbol(char16_t aSymbol, bool aSkipWS); + bool ExpectEndProperty(); + bool CheckEndProperty(); + nsSubstring* NextIdent(); + + // returns true when the stop symbol is found, and false for EOF + bool SkipUntil(char16_t aStopSymbol); + void SkipUntilOneOf(const char16_t* aStopSymbolChars); + // For each character in aStopSymbolChars from the end of the array + // to the start, calls SkipUntil with that character. + typedef AutoTArray StopSymbolCharStack; + void SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars); + // returns true if the stop symbol or EOF is found, and false for an + // unexpected ')', ']' or '}'; this not safe to call outside variable + // resolution, as it doesn't handle mismatched content + bool SkipBalancedContentUntil(char16_t aStopSymbol); + + void SkipRuleSet(bool aInsideBraces); + bool SkipAtRule(bool aInsideBlock); + bool SkipDeclaration(bool aCheckForBraces); + + void PushGroup(css::GroupRule* aRule); + void PopGroup(); + + bool ParseRuleSet(RuleAppendFunc aAppendFunc, void* aProcessData, + bool aInsideBraces = false); + bool ParseAtRule(RuleAppendFunc aAppendFunc, void* aProcessData, + bool aInAtRule); + bool ParseCharsetRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseImportRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseURLOrString(nsString& aURL); + bool GatherMedia(nsMediaList* aMedia, bool aInAtRule); + + enum eMediaQueryType { eMediaQueryNormal, + // Parsing an at rule + eMediaQueryAtRule, + // Attempt to consume a single media-condition and + // stop. Note that the spec defines "expression and/or + // expression" as one condition but "expression, + // expression" as two. + eMediaQuerySingleCondition }; + bool ParseMediaQuery(eMediaQueryType aMode, nsMediaQuery **aQuery, + bool *aHitStop); + bool ParseMediaQueryExpression(nsMediaQuery* aQuery); + void ProcessImport(const nsString& aURLSpec, + nsMediaList* aMedia, + RuleAppendFunc aAppendFunc, + void* aProcessData, + uint32_t aLineNumber, + uint32_t aColumnNumber); + bool ParseGroupRule(css::GroupRule* aRule, RuleAppendFunc aAppendFunc, + void* aProcessData); + bool ParseMediaRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aProcessData); + void ProcessNameSpace(const nsString& aPrefix, + const nsString& aURLSpec, RuleAppendFunc aAppendFunc, + void* aProcessData, + uint32_t aLineNumber, uint32_t aColumnNumber); + + bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, + void* aProcessData); + bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule); + bool ParseFontDescriptor(nsCSSFontFaceRule* aRule); + bool ParseFontDescriptorValue(nsCSSFontDesc aDescID, + nsCSSValue& aValue); + + bool ParsePageRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aProcessData); + already_AddRefed ParseKeyframeRule(); + bool ParseKeyframeSelectorList(InfallibleTArray& aSelectorList); + + bool ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseSupportsCondition(bool& aConditionMet); + bool ParseSupportsConditionNegation(bool& aConditionMet); + bool ParseSupportsConditionInParens(bool& aConditionMet); + bool ParseSupportsMozBoolPrefName(bool& aConditionMet); + bool ParseSupportsConditionInParensInsideParens(bool& aConditionMet); + bool ParseSupportsConditionTerms(bool& aConditionMet); + enum SupportsConditionTermOperator { eAnd, eOr }; + bool ParseSupportsConditionTermsAfterOperator( + bool& aConditionMet, + SupportsConditionTermOperator aOperator); + + bool ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseCounterStyleName(nsAString& aName, bool aForDefinition); + bool ParseCounterStyleNameValue(nsCSSValue& aValue); + bool ParseCounterDescriptor(nsCSSCounterStyleRule *aRule); + bool ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, + nsCSSValue& aValue); + bool ParseCounterRange(nsCSSValuePair& aPair); + + /** + * Parses the current input stream for a CSS token stream value and resolves + * any variable references using the variables in aVariables. + * + * @param aVariables The set of variable values to use when resolving variable + * references. + * @param aResult Out parameter that, if the function returns true, will be + * replaced with the resolved value. + * @return Whether aResult could be parsed successfully and variable reference + * substitution succeeded. + */ + bool ResolveValueWithVariableReferences( + const CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aResultFirstToken, + nsCSSTokenSerializationType& aResultLastToken); + // Helper function for ResolveValueWithVariableReferences. + bool ResolveValueWithVariableReferencesRec( + nsString& aResult, + nsCSSTokenSerializationType& aResultFirstToken, + nsCSSTokenSerializationType& aResultLastToken, + const CSSVariableValues* aVariables); + + enum nsSelectorParsingStatus { + // we have parsed a selector and we saw a token that cannot be + // part of a selector: + eSelectorParsingStatus_Done, + // we should continue parsing the selector: + eSelectorParsingStatus_Continue, + // we saw an unexpected token or token value, + // or we saw end-of-file with an unfinished selector: + eSelectorParsingStatus_Error + }; + nsSelectorParsingStatus ParseIDSelector(int32_t& aDataMask, + nsCSSSelector& aSelector); + + nsSelectorParsingStatus ParseClassSelector(int32_t& aDataMask, + nsCSSSelector& aSelector); + + // aPseudoElement and aPseudoElementArgs are the location where + // pseudo-elements (as opposed to pseudo-classes) are stored; + // pseudo-classes are stored on aSelector. aPseudoElement and + // aPseudoElementArgs must be non-null iff !aIsNegated. + nsSelectorParsingStatus ParsePseudoSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + bool aIsNegated, + nsIAtom** aPseudoElement, + nsAtomList** aPseudoElementArgs, + CSSPseudoElementType* aPseudoElementType); + + nsSelectorParsingStatus ParseAttributeSelector(int32_t& aDataMask, + nsCSSSelector& aSelector); + + nsSelectorParsingStatus ParseTypeOrUniversalSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + bool aIsNegated); + + nsSelectorParsingStatus ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType); + + nsSelectorParsingStatus ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType); + + nsSelectorParsingStatus ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType); + + nsSelectorParsingStatus ParseNegatedSimpleSelector(int32_t& aDataMask, + nsCSSSelector& aSelector); + + // If aStopChar is non-zero, the selector list is done when we hit + // aStopChar. Otherwise, it's done when we hit EOF. + bool ParseSelectorList(nsCSSSelectorList*& aListHead, + char16_t aStopChar); + bool ParseSelectorGroup(nsCSSSelectorList*& aListHead); + bool ParseSelector(nsCSSSelectorList* aList, char16_t aPrevCombinator); + + enum { + eParseDeclaration_InBraces = 1 << 0, + eParseDeclaration_AllowImportant = 1 << 1, + // The declaration we're parsing was generated by the CSSUnprefixingService: + eParseDeclaration_FromUnprefixingSvc = 1 << 2 + }; + enum nsCSSContextType { + eCSSContext_General, + eCSSContext_Page + }; + + already_AddRefed + ParseDeclarationBlock(uint32_t aFlags, + nsCSSContextType aContext = eCSSContext_General); + bool ParseDeclaration(css::Declaration* aDeclaration, + uint32_t aFlags, + bool aMustCallValueAppended, + bool* aChanged, + nsCSSContextType aContext = eCSSContext_General); + + // A "prefix-aware" wrapper for nsCSSKeywords::LookupKeyword(). + // Use this instead of LookupKeyword() if you might be parsing an unprefixed + // property (like "display") for which we emulate a vendor-prefixed value + // (like "-webkit-box"). + nsCSSKeyword LookupKeywordPrefixAware(nsAString& aKeywordStr, + const KTableEntry aKeywordTable[]); + + bool ShouldUseUnprefixingService() const; + bool ParsePropertyWithUnprefixingService(const nsAString& aPropertyName, + css::Declaration* aDeclaration, + uint32_t aFlags, + bool aMustCallValueAppended, + bool* aChanged, + nsCSSContextType aContext); + // When we detect a webkit-prefixed gradient expression, this function can be + // used to parse its body into outparam |aValue|, with the help of the + // CSSUnprefixingService. + // Only call if ShouldUseUnprefixingService() returns true. + bool ParseWebkitPrefixedGradientWithService(nsAString& aPrefixedFuncName, + nsCSSValue& aValue); + + bool ParseProperty(nsCSSPropertyID aPropID); + bool ParsePropertyByFunction(nsCSSPropertyID aPropID); + CSSParseResult ParseSingleValueProperty(nsCSSValue& aValue, + nsCSSPropertyID aPropID); + bool ParseSingleValuePropertyByFunction(nsCSSValue& aValue, + nsCSSPropertyID aPropID); + + // This is similar to ParseSingleValueProperty but only works for + // properties that are parsed with ParseBoxProperties or + // ParseGroupedBoxProperty. + // + // Only works with variants with the following flags: + // A, C, H, K, L, N, P, CALC. + CSSParseResult ParseBoxProperty(nsCSSValue& aValue, + nsCSSPropertyID aPropID); + + enum PriorityParsingStatus { + ePriority_None, + ePriority_Important, + ePriority_Error + }; + PriorityParsingStatus ParsePriority(); + +#ifdef MOZ_XUL + bool ParseTreePseudoElement(nsAtomList **aPseudoElementArgs); +#endif + + // Property specific parsing routines + bool ParseImageLayers(const nsCSSPropertyID aTable[]); + + struct ImageLayersShorthandParseState { + nsCSSValue& mColor; + nsCSSValueList* mImage; + nsCSSValuePairList* mRepeat; + nsCSSValueList* mAttachment; // A property for background layer only + nsCSSValueList* mClip; + nsCSSValueList* mOrigin; + nsCSSValueList* mPositionX; + nsCSSValueList* mPositionY; + nsCSSValuePairList* mSize; + nsCSSValueList* mComposite; // A property for mask layer only + nsCSSValueList* mMode; // A property for mask layer only + ImageLayersShorthandParseState( + nsCSSValue& aColor, nsCSSValueList* aImage, nsCSSValuePairList* aRepeat, + nsCSSValueList* aAttachment, nsCSSValueList* aClip, + nsCSSValueList* aOrigin, + nsCSSValueList* aPositionX, nsCSSValueList* aPositionY, + nsCSSValuePairList* aSize, nsCSSValueList* aComposite, + nsCSSValueList* aMode) : + mColor(aColor), mImage(aImage), mRepeat(aRepeat), + mAttachment(aAttachment), mClip(aClip), mOrigin(aOrigin), + mPositionX(aPositionX), mPositionY(aPositionY), + mSize(aSize), mComposite(aComposite), + mMode(aMode) {}; + }; + + bool IsFunctionTokenValidForImageLayerImage(const nsCSSToken& aToken) const; + bool ParseImageLayersItem(ImageLayersShorthandParseState& aState, + const nsCSSPropertyID aTable[]); + + bool ParseValueList(nsCSSPropertyID aPropID); // a single value prop-id + bool ParseImageLayerRepeat(nsCSSPropertyID aPropID); + bool ParseImageLayerRepeatValues(nsCSSValuePair& aValue); + bool ParseImageLayerPosition(const nsCSSPropertyID aTable[]); + bool ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal); + + // ParseBoxPositionValues parses the CSS 2.1 background-position syntax, + // which is still used by some properties. See ParsePositionValue + // for the css3-background syntax. + bool ParseBoxPositionValues(nsCSSValuePair& aOut, bool aAcceptsInherit, + bool aAllowExplicitCenter = true); // deprecated + + // ParsePositionValue parses a CSS value, which is used by + // the 'background-position' property. + bool ParsePositionValue(nsCSSValue& aOut); + bool ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY); + + bool ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal); + bool ParseImageLayerSize(nsCSSPropertyID aPropID); + bool ParseImageLayerSizeValues(nsCSSValuePair& aOut); + bool ParseBorderColor(); + bool ParseBorderColors(nsCSSPropertyID aProperty); + void SetBorderImageInitialValues(); + bool ParseBorderImageRepeat(bool aAcceptsInherit); + // If ParseBorderImageSlice returns false, aConsumedTokens indicates + // whether or not any tokens were consumed (in other words, was the property + // in error or just not present). If ParseBorderImageSlice returns true + // aConsumedTokens is always true. + bool ParseBorderImageSlice(bool aAcceptsInherit, bool* aConsumedTokens); + bool ParseBorderImageWidth(bool aAcceptsInherit); + bool ParseBorderImageOutset(bool aAcceptsInherit); + bool ParseBorderImage(); + bool ParseBorderSpacing(); + bool ParseBorderSide(const nsCSSPropertyID aPropIDs[], + bool aSetAllSides); + bool ParseBorderStyle(); + bool ParseBorderWidth(); + + bool ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask); + bool ParseCalcAdditiveExpression(nsCSSValue& aValue, + uint32_t& aVariantMask); + bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue, + uint32_t& aVariantMask, + bool *aHadFinalWS); + bool ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask); + bool RequireWhitespace(); + + // For "flex" shorthand property, defined in CSS Flexbox spec + bool ParseFlex(); + // For "flex-flow" shorthand property, defined in CSS Flexbox spec + bool ParseFlexFlow(); + + // CSS Grid + bool ParseGridAutoFlow(); + + // Parse a expression. + // If successful, either leave aValue untouched, + // to indicate that we parsed the empty list, + // or set it to a eCSSUnit_List of eCSSUnit_Ident. + // + // To parse an optional (ie. if not finding an open bracket + // is considered the same as an empty list), + // treat CSSParseResult::NotFound the same as CSSParseResult::Ok. + // + // If aValue is already a eCSSUnit_List, append to that list. + CSSParseResult ParseGridLineNames(nsCSSValue& aValue); + bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr); + bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue); + + CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue); + // eFixedTrackSize in aFlags makes it parse a . + CSSParseResult ParseGridTrackSize(nsCSSValue& aValue, + GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize); + + bool ParseGridAutoColumnsRows(nsCSSPropertyID aPropID); + bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr); + bool ParseGridTrackRepeatIntro(bool aForSubgrid, + int32_t* aRepetitions, + Maybe* aRepeatAutoEnum); + + // Assuming a [ ? ] has already been parsed, + // parse the rest of a . + // + // This exists because [ ? ] is ambiguous in the 'grid-template' + // shorthand: it can be either the start of a (in + // a <'grid-template-rows'>) or of the intertwined syntax that sets both + // grid-template-rows and grid-template-areas. + // + // On success, |aValue| will be a list of odd length >= 3, + // starting with a (which is itself a list) + // and alternating between that and . + bool ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue, + const nsCSSValue& aFirstLineNames, + GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList); + + bool ParseGridTrackList(nsCSSPropertyID aPropID, + GridTrackListFlags aFlags = GridTrackListFlags::eDefaultTrackList); + bool ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID); + + // |aAreaIndices| is a lookup table to help us parse faster, + // mapping area names to indices in |aResult.mNamedAreas|. + bool ParseGridTemplateAreasLine(const nsAutoString& aInput, + css::GridTemplateAreasValue* aResult, + nsDataHashtable& aAreaIndices); + bool ParseGridTemplateAreas(); + bool ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand); + bool ParseGridTemplate(bool aForGridShorthand = false); + bool ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames); + bool ParseGrid(); + CSSParseResult ParseGridShorthandAutoProps(int32_t aAutoFlowAxis); + bool ParseGridLine(nsCSSValue& aValue); + bool ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID); + bool ParseGridColumnRow(nsCSSPropertyID aStartPropID, + nsCSSPropertyID aEndPropID); + bool ParseGridArea(); + bool ParseGridGap(); + + bool ParseInitialLetter(); + + // parsing 'align/justify-items/self' from the css-align spec + bool ParseAlignJustifyPosition(nsCSSValue& aResult, + const KTableEntry aTable[]); + bool ParseJustifyItems(); + bool ParseAlignItems(); + bool ParseAlignJustifySelf(nsCSSPropertyID aPropID); + // parsing 'align/justify-content' from the css-align spec + bool ParseAlignJustifyContent(nsCSSPropertyID aPropID); + bool ParsePlaceContent(); + bool ParsePlaceItems(); + bool ParsePlaceSelf(); + + // for 'clip' and '-moz-image-region' + bool ParseRect(nsCSSPropertyID aPropID); + bool ParseColumns(); + bool ParseContain(nsCSSValue& aValue); + bool ParseContent(); + bool ParseCounterData(nsCSSPropertyID aPropID); + bool ParseCursor(); + bool ParseFont(); + bool ParseFontSynthesis(nsCSSValue& aValue); + bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue); + bool ParseFontVariantAlternates(nsCSSValue& aValue); + bool MergeBitmaskValue(int32_t aNewValue, const int32_t aMasks[], + int32_t& aMergedValue); + bool ParseBitmaskValues(nsCSSValue& aValue, + const KTableEntry aKeywordTable[], + const int32_t aMasks[]); + bool ParseFontVariantEastAsian(nsCSSValue& aValue); + bool ParseFontVariantLigatures(nsCSSValue& aValue); + bool ParseFontVariantNumeric(nsCSSValue& aValue); + bool ParseFontVariant(); + bool ParseFontWeight(nsCSSValue& aValue); + bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword, bool& aQuoted); + bool ParseFamily(nsCSSValue& aValue); + bool ParseFontFeatureSettings(nsCSSValue& aValue); + bool ParseFontSrc(nsCSSValue& aValue); + bool ParseFontSrcFormat(InfallibleTArray& values); + bool ParseFontRanges(nsCSSValue& aValue); + bool ParseListStyle(); + bool ParseListStyleType(nsCSSValue& aValue); + bool ParseMargin(); + bool ParseClipPath(nsCSSValue& aValue); + bool ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues = false); + bool ParseObjectPosition(); + bool ParseOutline(); + bool ParseOverflow(); + bool ParsePadding(); + bool ParseQuotes(); + bool ParseTextAlign(nsCSSValue& aValue, + const KTableEntry aTable[]); + bool ParseTextAlign(nsCSSValue& aValue); + bool ParseTextAlignLast(nsCSSValue& aValue); + bool ParseTextDecoration(); + bool ParseTextDecorationLine(nsCSSValue& aValue); + bool ParseTextEmphasis(); + bool ParseTextEmphasisPosition(nsCSSValue& aValue); + bool ParseTextEmphasisStyle(nsCSSValue& aValue); + bool ParseTextCombineUpright(nsCSSValue& aValue); + bool ParseTextOverflow(nsCSSValue& aValue); + bool ParseTouchAction(nsCSSValue& aValue); + + bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow); + bool ParseShadowList(nsCSSPropertyID aProperty); + bool ParseShapeOutside(nsCSSValue& aValue); + bool ParseTransitionProperty(); + bool ParseTransitionTimingFunctionValues(nsCSSValue& aValue); + bool ParseTransitionTimingFunctionValueComponent(float& aComponent, + char aStop, + bool aIsXPoint); + bool ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue); + enum ParseAnimationOrTransitionShorthandResult { + eParseAnimationOrTransitionShorthand_Values, + eParseAnimationOrTransitionShorthand_Inherit, + eParseAnimationOrTransitionShorthand_Error + }; + ParseAnimationOrTransitionShorthandResult + ParseAnimationOrTransitionShorthand(const nsCSSPropertyID* aProperties, + const nsCSSValue* aInitialValues, + nsCSSValue* aValues, + size_t aNumProperties); + bool ParseTransition(); + bool ParseAnimation(); + bool ParseWillChange(); + + bool ParsePaint(nsCSSPropertyID aPropID); + bool ParseDasharray(); + bool ParseMarker(); + bool ParsePaintOrder(); + bool ParseAll(); + bool ParseScrollSnapType(); + bool ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID); + bool ParseScrollSnapDestination(nsCSSValue& aValue); + bool ParseScrollSnapCoordinate(nsCSSValue& aValue); + bool ParseWebkitTextStroke(); + + /** + * Parses a variable value from a custom property declaration. + * + * @param aType Out parameter into which will be stored the type of variable + * value, indicating whether the parsed value was a token stream or one of + * the CSS-wide keywords. + * @param aValue Out parameter into which will be stored the token stream + * as a string, if the parsed custom property did take a token stream. + * @return Whether parsing succeeded. + */ + bool ParseVariableDeclaration(CSSVariableDeclarations::Type* aType, + nsString& aValue); + + /** + * Parses a CSS variable value. This could be 'initial', 'inherit', 'unset' + * or a token stream, which may or may not include variable references. + * + * @param aType Out parameter into which the type of the variable value + * will be stored. + * @param aDropBackslash Out parameter indicating whether during variable + * value parsing there was a trailing backslash before EOF that must + * be dropped when serializing the variable value. + * @param aImpliedCharacters Out parameter appended to which will be any + * characters that were implied when encountering EOF and which + * must be included at the end of the serialized variable value. + * @param aFunc A callback function to invoke when a variable reference + * is encountered. May be null. Arguments are the variable name + * and the aData argument passed in to this function. + * @param User data to pass in to the callback. + * @return Whether parsing succeeded. + */ + bool ParseValueWithVariables(CSSVariableDeclarations::Type* aType, + bool* aDropBackslash, + nsString& aImpliedCharacters, + void (*aFunc)(const nsAString&, void*), + void* aData); + + /** + * Returns whether the scanner dropped a backslash just before EOF. + */ + bool BackslashDropped(); + + /** + * Calls AppendImpliedEOFCharacters on mScanner. + */ + void AppendImpliedEOFCharacters(nsAString& aResult); + + // Reused utility parsing routines + void AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue); + bool ParseBoxProperties(const nsCSSPropertyID aPropIDs[]); + bool ParseGroupedBoxProperty(int32_t aVariantMask, + nsCSSValue& aValue, + uint32_t aRestrictions); + bool ParseBoxCornerRadius(const nsCSSPropertyID aPropID); + bool ParseBoxCornerRadiiInternals(nsCSSValue array[]); + bool ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]); + + int32_t ParseChoice(nsCSSValue aValues[], + const nsCSSPropertyID aPropIDs[], int32_t aNumIDs); + + CSSParseResult ParseColor(nsCSSValue& aValue); + + template + bool ParseRGBColor(ComponentType& aR, + ComponentType& aG, + ComponentType& aB, + ComponentType& aA); + bool ParseHSLColor(float& aHue, float& aSaturation, float& aLightness, + float& aOpacity); + + // The ParseColorOpacityAndCloseParen methods below attempt to parse an + // optional [ separator ] expression, followed by a + // close-parenthesis, at the end of a css color function (e.g. "rgba()" or + // "hsla()"). If these functions simply encounter a close-parenthesis (without + // any [separator ]), they will still succeed (i.e. return true), + // with outparam 'aOpacity' set to a default opacity value (fully-opaque). + // + // The range of opacity component is [0, 255], and the default opacity value + // is 255 (fully-opaque) for this function. + bool ParseColorOpacityAndCloseParen(uint8_t& aOpacity, + char aSeparator); + // Similar to the previous one, but the range of opacity component is + // [0.0f, 1.0f] and the default opacity value is 1.0f (fully-opaque). + bool ParseColorOpacityAndCloseParen(float& aOpacity, + char aSeparator); + + // Parse a color component. The range of color component is [0, 255]. + // If |aSeparator| is provided, this function will also attempt to parse that + // character after parsing the color component. + bool ParseColorComponent(uint8_t& aComponent, Maybe aSeparator); + // Similar to the previous one, but parse a color component. + // The range of color component is [0.0f, 1.0f]. + bool ParseColorComponent(float& aComponent, Maybe aSeparator); + + // Parse a component. + // = | + // The unit of outparam 'aAngle' is degree. Assume the unit to be degree if an + // unitless is parsed. + bool ParseHue(float& aAngle); + + bool ParseEnum(nsCSSValue& aValue, + const KTableEntry aKeywordTable[]); + + // A special ParseEnum for the CSS Box Alignment properties that have + // 'baseline' values. In addition to the keywords in aKeywordTable, it also + // parses 'first baseline' and 'last baseline' as a single value. + // (aKeywordTable must contain 'baseline') + bool ParseAlignEnum(nsCSSValue& aValue, const KTableEntry aKeywordTable[]); + + // Variant parsing methods + CSSParseResult ParseVariant(nsCSSValue& aValue, + uint32_t aVariantMask, + const KTableEntry aKeywordTable[]); + CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[], + uint32_t aRestrictions); + CSSParseResult ParseNonNegativeVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]); + CSSParseResult ParseOneOrLargerVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]); + + // Variant parsing methods that are guaranteed to UngetToken any token + // consumed on failure + bool ParseSingleTokenVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseVariant for variants in VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + bool ParseSingleTokenVariantWithRestrictions( + nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[], + uint32_t aRestrictions) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseVariantWithRestrictions for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseVariantWithRestrictions(aValue, aVariantMask, aKeywordTable, + aRestrictions); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + bool ParseSingleTokenNonNegativeVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseNonNegativeVariant for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + bool ParseSingleTokenOneOrLargerVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseOneOrLargerVariant for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + + // Helpers for some common ParseSingleTokenNonNegativeVariant calls. + bool ParseNonNegativeInteger(nsCSSValue& aValue) + { + return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr); + } + bool ParseNonNegativeNumber(nsCSSValue& aValue) + { + return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr); + } + + // Helpers for some common ParseSingleTokenOneOrLargerVariant calls. + bool ParseOneOrLargerInteger(nsCSSValue& aValue) + { + return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_INTEGER, nullptr); + } + bool ParseOneOrLargerNumber(nsCSSValue& aValue) + { + return ParseSingleTokenOneOrLargerVariant(aValue, VARIANT_NUMBER, nullptr); + } + + // http://dev.w3.org/csswg/css-values/#custom-idents + // Parse an identifier that is none of: + // * a CSS-wide keyword + // * "default" + // * a keyword in |aExcludedKeywords| + // * a keyword in |aPropertyKTable| + // + // |aExcludedKeywords| is an array of nsCSSKeyword + // that ends with a eCSSKeyword_UNKNOWN marker. + // + // |aPropertyKTable| can be used if some of the keywords to exclude + // also appear in an existing nsCSSProps::KTableEntry, + // to avoid duplicating them. + bool ParseCustomIdent(nsCSSValue& aValue, + const nsAutoString& aIdentValue, + const nsCSSKeyword aExcludedKeywords[] = nullptr, + const nsCSSProps::KTableEntry aPropertyKTable[] = nullptr); + bool ParseCounter(nsCSSValue& aValue); + bool ParseAttr(nsCSSValue& aValue); + bool ParseSymbols(nsCSSValue& aValue); + bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL); + bool TranslateDimension(nsCSSValue& aValue, uint32_t aVariantMask, + float aNumber, const nsString& aUnit); + bool ParseImageOrientation(nsCSSValue& aAngle); + bool ParseImageRect(nsCSSValue& aImage); + bool ParseElement(nsCSSValue& aValue); + bool ParseColorStop(nsCSSValueGradient* aGradient); + + enum GradientParsingFlags { + eGradient_Repeating = 1 << 0, // repeating-{linear|radial}-gradient + eGradient_MozLegacy = 1 << 1, // -moz-{linear|radial}-gradient + eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient + + // Mask to catch both "legacy" flags: + eGradient_AnyLegacy = eGradient_MozLegacy | eGradient_WebkitLegacy + }; + bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags); + bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags); + bool IsLegacyGradientLine(const nsCSSTokenType& aType, + const nsString& aId); + bool ParseGradientColorStops(nsCSSValueGradient* aGradient, + nsCSSValue& aValue); + + // For the ancient "-webkit-gradient(linear|radial, ...)" syntax: + bool ParseWebkitGradientPointComponent(nsCSSValue& aComponent, + bool aIsHorizontal); + bool ParseWebkitGradientPoint(nsCSSValuePair& aPoint); + bool ParseWebkitGradientRadius(float& aRadius); + bool ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient); + bool ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient); + void FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient, + const nsCSSValuePair& aStartPoint, + const nsCSSValuePair& aSecondPoint); + void FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient, + const nsCSSValuePair& aFirstCenter, + const nsCSSValuePair& aSecondCenter, + const float aFirstRadius, + const float aSecondRadius); + bool ParseWebkitGradient(nsCSSValue& aValue); + + void SetParsingCompoundProperty(bool aBool) { + mParsingCompoundProperty = aBool; + } + bool IsParsingCompoundProperty(void) const { + return mParsingCompoundProperty; + } + + /* Functions for basic shapes */ + bool ParseReferenceBoxAndBasicShape(nsCSSValue& aValue, + const KTableEntry aBoxKeywordTable[]); + bool ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens); + bool ParsePolygonFunction(nsCSSValue& aValue); + bool ParseCircleOrEllipseFunction(nsCSSKeyword, nsCSSValue& aValue); + bool ParseInsetFunction(nsCSSValue& aValue); + // We parse position values differently for basic-shape, by expanding defaults + // and replacing keywords with percentages + bool ParsePositionValueForBasicShape(nsCSSValue& aOut); + + + /* Functions for transform Parsing */ + bool ParseSingleTransform(bool aIsPrefixed, bool aDisallowRelativeValues, + nsCSSValue& aValue); + bool ParseFunction(nsCSSKeyword aFunction, const uint32_t aAllowedTypes[], + uint32_t aVariantMaskAll, uint16_t aMinElems, + uint16_t aMaxElems, nsCSSValue &aValue); + bool ParseFunctionInternals(const uint32_t aVariantMask[], + uint32_t aVariantMaskAll, + uint16_t aMinElems, + uint16_t aMaxElems, + InfallibleTArray& aOutput); + + /* Functions for transform-origin/perspective-origin Parsing */ + bool ParseTransformOrigin(bool aPerspective); + + /* Functions for filter parsing */ + bool ParseFilter(); + bool ParseSingleFilter(nsCSSValue* aValue); + bool ParseDropShadow(nsCSSValue* aValue); + + /* Find and return the namespace ID associated with aPrefix. + If aPrefix has not been declared in an @namespace rule, returns + kNameSpaceID_Unknown. */ + int32_t GetNamespaceIdForPrefix(const nsString& aPrefix); + + /* Find the correct default namespace, and set it on aSelector. */ + void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector); + + // Current token. The value is valid after calling GetToken and invalidated + // by UngetToken. + nsCSSToken mToken; + + // Our scanner. + nsCSSScanner* mScanner; + + // Our error reporter. + css::ErrorReporter* mReporter; + + // The URI to be used as a base for relative URIs. + nsCOMPtr mBaseURI; + + // The URI to be used as an HTTP "Referer" and for error reporting. + nsCOMPtr mSheetURI; + + // The principal of the sheet involved + nsCOMPtr mSheetPrincipal; + + // The sheet we're parsing into + RefPtr mSheet; + + // Used for @import rules + css::Loader* mChildLoader; // not ref counted, it owns us + + // Any sheets we may reuse when parsing an @import. + css::LoaderReusableStyleSheets* mReusableSheets; + + // Sheet section we're in. This is used to enforce correct ordering of the + // various rule types (eg the fact that a @charset rule must come before + // anything else). Note that there are checks of similar things in various + // places in CSSStyleSheet.cpp (e.g in insertRule, RebuildChildList). + enum nsCSSSection { + eCSSSection_Charset, + eCSSSection_Import, + eCSSSection_NameSpace, + eCSSSection_General + }; + nsCSSSection mSection; + + nsXMLNameSpaceMap *mNameSpaceMap; // weak, mSheet owns it + + // After an UngetToken is done this flag is true. The next call to + // GetToken clears the flag. + bool mHavePushBack : 1; + + // True if we are in quirks mode; false in standards or almost standards mode + bool mNavQuirkMode : 1; + + // True when the hashless color quirk applies. + bool mHashlessColorQuirk : 1; + + // True when the unitless length quirk applies. + bool mUnitlessLengthQuirk : 1; + + // Controls access to nonstandard style constructs that are not safe + // for use on the public Web but necessary in UA sheets and/or + // useful in user sheets. The only values stored in this field are + // 0, 1, and 2; three bits are allocated to avoid issues should the + // enum type be signed. + css::SheetParsingMode mParsingMode : 3; + + // True if we are in parsing rules for the chrome. + bool mIsChrome : 1; + + // True if viewport units should be allowed. + bool mViewportUnitsEnabled : 1; + + // True for parsing media lists for HTML attributes, where we have to + // ignore CSS comments. + bool mHTMLMediaMode : 1; + + // This flag is set when parsing a non-box shorthand; it's used to not apply + // some quirks during shorthand parsing + bool mParsingCompoundProperty : 1; + + // True if we are in the middle of parsing an @supports condition. + // This is used to avoid recording the input stream when variable references + // are encountered in a property declaration in the @supports condition. + bool mInSupportsCondition : 1; + + // True if we are somewhere within a @supports rule whose condition is + // false. + bool mInFailingSupportsRule : 1; + + // True if we will suppress all parse errors (except unexpected EOFs). + // This is used to prevent errors for declarations inside a failing + // @supports rule. + bool mSuppressErrors : 1; + + // True if any parsing of URL values requires a sheet principal to have + // been passed in the nsCSSScanner constructor. This is usually the case. + // It can be set to false, for example, when we create an nsCSSParser solely + // to parse a property value to test it for syntactic correctness. When + // false, an assertion that mSheetPrincipal is non-null is skipped. Should + // not be set to false if any nsCSSValues created during parsing can escape + // out of the parser. + bool mSheetPrincipalRequired; + + // This enum helps us track whether we've unprefixed "display: -webkit-box" + // (treating it as "display: flex") in an earlier declaration within a series + // of declarations. (This only impacts behavior when the function + // "ShouldUseUnprefixingService()" returns true, and that should only happen + // for a short whitelist of origins.) + enum WebkitBoxUnprefixState : uint8_t { + eNotParsingDecls, // We are *not* currently parsing a sequence of + // CSS declarations. (default state) + + // The next two enum values indicate that we *are* currently parsing a + // sequence of declarations (in ParseDeclarations or ParseDeclarationBlock) + // and... + eHaveNotUnprefixed, // ...we have not unprefixed 'display:-webkit-box' in + // this sequence of CSS declarations. + eHaveUnprefixed // ...we *have* unprefixed 'display:-webkit-box' earlier in + // this sequence of CSS declarations. + }; + WebkitBoxUnprefixState mWebkitBoxUnprefixState; + + // Stack of rule groups; used for @media and such. + InfallibleTArray > mGroupStack; + + // During the parsing of a property (which may be a shorthand), the data + // are stored in |mTempData|. (It is needed to ensure that parser + // errors cause the data to be ignored, and to ensure that a + // non-'!important' declaration does not override an '!important' + // one.) + nsCSSExpandedDataBlock mTempData; + + // All data from successfully parsed properties are placed into |mData|. + nsCSSExpandedDataBlock mData; + +public: + // Used from nsCSSParser constructors and destructors + CSSParserImpl* mNextFree; +}; + +static void AssignRuleToPointer(css::Rule* aRule, void* aPointer) +{ + css::Rule **pointer = static_cast(aPointer); + NS_ADDREF(*pointer = aRule); +} + +static void AppendRuleToSheet(css::Rule* aRule, void* aParser) +{ + CSSParserImpl* parser = (CSSParserImpl*) aParser; + parser->AppendRule(aRule); +} + +#define REPORT_UNEXPECTED(msg_) \ + { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_); } + +#define REPORT_UNEXPECTED_P(msg_, param_) \ + { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_); } + +#define REPORT_UNEXPECTED_P_V(msg_, param_, value_) \ + { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, param_, value_); } + +#define REPORT_UNEXPECTED_TOKEN(msg_) \ + { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken); } + +#define REPORT_UNEXPECTED_TOKEN_CHAR(msg_, ch_) \ + { if (!mSuppressErrors) mReporter->ReportUnexpected(#msg_, mToken, ch_); } + +#define REPORT_UNEXPECTED_EOF(lf_) \ + mReporter->ReportUnexpectedEOF(#lf_) + +#define REPORT_UNEXPECTED_EOF_CHAR(ch_) \ + mReporter->ReportUnexpectedEOF(ch_) + +#define OUTPUT_ERROR() \ + mReporter->OutputError() + +#define OUTPUT_ERROR_WITH_POSITION(linenum_, lineoff_) \ + mReporter->OutputError(linenum_, lineoff_) + +#define CLEAR_ERROR() \ + mReporter->ClearError() + +CSSParserImpl::CSSParserImpl() + : mToken(), + mScanner(nullptr), + mReporter(nullptr), + mChildLoader(nullptr), + mReusableSheets(nullptr), + mSection(eCSSSection_Charset), + mNameSpaceMap(nullptr), + mHavePushBack(false), + mNavQuirkMode(false), + mHashlessColorQuirk(false), + mUnitlessLengthQuirk(false), + mParsingMode(css::eAuthorSheetFeatures), + mIsChrome(false), + mViewportUnitsEnabled(true), + mHTMLMediaMode(false), + mParsingCompoundProperty(false), + mInSupportsCondition(false), + mInFailingSupportsRule(false), + mSuppressErrors(false), + mSheetPrincipalRequired(true), + mWebkitBoxUnprefixState(eNotParsingDecls), + mNextFree(nullptr) +{ +} + +CSSParserImpl::~CSSParserImpl() +{ + mData.AssertInitialState(); + mTempData.AssertInitialState(); +} + +nsresult +CSSParserImpl::SetStyleSheet(CSSStyleSheet* aSheet) +{ + if (aSheet != mSheet) { + // Switch to using the new sheet, if any + mGroupStack.Clear(); + mSheet = aSheet; + if (mSheet) { + mNameSpaceMap = mSheet->GetNameSpaceMap(); + } else { + mNameSpaceMap = nullptr; + } + } else if (mSheet) { + mNameSpaceMap = mSheet->GetNameSpaceMap(); + } + + return NS_OK; +} + +nsIDocument* +CSSParserImpl::GetDocument() +{ + if (!mSheet) { + return nullptr; + } + return mSheet->GetDocument(); +} + +nsresult +CSSParserImpl::SetQuirkMode(bool aQuirkMode) +{ + mNavQuirkMode = aQuirkMode; + return NS_OK; +} + +nsresult +CSSParserImpl::SetChildLoader(mozilla::css::Loader* aChildLoader) +{ + mChildLoader = aChildLoader; // not ref counted, it owns us + return NS_OK; +} + +void +CSSParserImpl::Reset() +{ + NS_ASSERTION(!mScanner, "resetting with scanner active"); + SetStyleSheet(nullptr); + SetQuirkMode(false); + SetChildLoader(nullptr); +} + +void +CSSParserImpl::InitScanner(nsCSSScanner& aScanner, + css::ErrorReporter& aReporter, + nsIURI* aSheetURI, nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal) +{ + NS_PRECONDITION(!mHTMLMediaMode, "Bad initial state"); + NS_PRECONDITION(!mParsingCompoundProperty, "Bad initial state"); + NS_PRECONDITION(!mScanner, "already have scanner"); + + mScanner = &aScanner; + mReporter = &aReporter; + mScanner->SetErrorReporter(mReporter); + + mBaseURI = aBaseURI; + mSheetURI = aSheetURI; + mSheetPrincipal = aSheetPrincipal; + mHavePushBack = false; +} + +void +CSSParserImpl::ReleaseScanner() +{ + mScanner = nullptr; + mReporter = nullptr; + mBaseURI = nullptr; + mSheetURI = nullptr; + mSheetPrincipal = nullptr; +} + +nsresult +CSSParserImpl::ParseSheet(const nsAString& aInput, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + uint32_t aLineNumber, + css::LoaderReusableStyleSheets* aReusableSheets) +{ + NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); + NS_PRECONDITION(aBaseURI, "need base URI"); + NS_PRECONDITION(aSheetURI, "need sheet URI"); + NS_PRECONDITION(mSheet, "Must have sheet to parse into"); + NS_ENSURE_STATE(mSheet); + +#ifdef DEBUG + nsIURI* uri = mSheet->GetSheetURI(); + bool equal; + NS_ASSERTION(NS_SUCCEEDED(aSheetURI->Equals(uri, &equal)) && equal, + "Sheet URI does not match passed URI"); + NS_ASSERTION(NS_SUCCEEDED(mSheet->Principal()->Equals(aSheetPrincipal, + &equal)) && + equal, + "Sheet principal does not match passed principal"); +#endif + + nsCSSScanner scanner(aInput, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); + InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); + + int32_t ruleCount = mSheet->StyleRuleCount(); + if (0 < ruleCount) { + const css::Rule* lastRule = mSheet->GetStyleRuleAt(ruleCount - 1); + if (lastRule) { + switch (lastRule->GetType()) { + case css::Rule::CHARSET_RULE: + case css::Rule::IMPORT_RULE: + mSection = eCSSSection_Import; + break; + case css::Rule::NAMESPACE_RULE: + mSection = eCSSSection_NameSpace; + break; + default: + mSection = eCSSSection_General; + break; + } + } + } + else { + mSection = eCSSSection_Charset; // sheet is empty, any rules are fair + } + + mParsingMode = mSheet->ParsingMode(); + mIsChrome = dom::IsChromeURI(aSheetURI); + mReusableSheets = aReusableSheets; + + nsCSSToken* tk = &mToken; + for (;;) { + // Get next non-whitespace token + if (!GetToken(true)) { + OUTPUT_ERROR(); + break; + } + if (eCSSToken_HTMLComment == tk->mType) { + continue; // legal here only + } + if (eCSSToken_AtKeyword == tk->mType) { + ParseAtRule(AppendRuleToSheet, this, false); + continue; + } + UngetToken(); + if (ParseRuleSet(AppendRuleToSheet, this)) { + mSection = eCSSSection_General; + } + } + ReleaseScanner(); + + mParsingMode = css::eAuthorSheetFeatures; + mIsChrome = false; + mReusableSheets = nullptr; + + return NS_OK; +} + +/** + * Determines whether the identifier contained in the given string is a + * vendor-specific identifier, as described in CSS 2.1 section 4.1.2.1. + */ +static bool +NonMozillaVendorIdentifier(const nsAString& ident) +{ + return (ident.First() == char16_t('-') && + !StringBeginsWith(ident, NS_LITERAL_STRING("-moz-"))) || + ident.First() == char16_t('_'); + +} + +already_AddRefed +CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue, + nsIURI* aDocURI, + nsIURI* aBaseURI, + nsIPrincipal* aNodePrincipal) +{ + NS_PRECONDITION(aNodePrincipal, "Must have principal here!"); + NS_PRECONDITION(aBaseURI, "need base URI"); + + // XXX line number? + nsCSSScanner scanner(aAttributeValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI); + InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal); + + mSection = eCSSSection_General; + + uint32_t parseFlags = eParseDeclaration_AllowImportant; + + RefPtr declaration = ParseDeclarationBlock(parseFlags); + + ReleaseScanner(); + + return declaration.forget(); +} + +nsresult +CSSParserImpl::ParseDeclarations(const nsAString& aBuffer, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged) +{ + *aChanged = false; + + NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); + + nsCSSScanner scanner(aBuffer, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); + InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); + + MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls, + "Someone forgot to clear mWebkitBoxUnprefixState!"); + AutoRestore autoRestore(mWebkitBoxUnprefixState); + mWebkitBoxUnprefixState = eHaveNotUnprefixed; + + mSection = eCSSSection_General; + + mData.AssertInitialState(); + aDeclaration->ClearData(); + // We could check if it was already empty, but... + *aChanged = true; + + for (;;) { + // If we cleared the old decl, then we want to be calling + // ValueAppended as we parse. + if (!ParseDeclaration(aDeclaration, eParseDeclaration_AllowImportant, + true, aChanged)) { + if (!SkipDeclaration(false)) { + break; + } + } + } + + aDeclaration->CompressFrom(&mData); + ReleaseScanner(); + return NS_OK; +} + +nsresult +CSSParserImpl::ParseRule(const nsAString& aRule, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Rule** aResult) +{ + NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); + NS_PRECONDITION(aBaseURI, "need base URI"); + + *aResult = nullptr; + + nsCSSScanner scanner(aRule, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); + InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); + + mSection = eCSSSection_Charset; // callers are responsible for rejecting invalid rules. + + nsCSSToken* tk = &mToken; + // Get first non-whitespace token + nsresult rv = NS_OK; + if (!GetToken(true)) { + REPORT_UNEXPECTED(PEParseRuleWSOnly); + OUTPUT_ERROR(); + rv = NS_ERROR_DOM_SYNTAX_ERR; + } else { + if (eCSSToken_AtKeyword == tk->mType) { + // FIXME: perhaps aInsideBlock should be true when we are? + ParseAtRule(AssignRuleToPointer, aResult, false); + } else { + UngetToken(); + ParseRuleSet(AssignRuleToPointer, aResult); + } + + if (*aResult && GetToken(true)) { + // garbage after rule + REPORT_UNEXPECTED_TOKEN(PERuleTrailing); + NS_RELEASE(*aResult); + } + + if (!*aResult) { + rv = NS_ERROR_DOM_SYNTAX_ERR; + OUTPUT_ERROR(); + } + } + + ReleaseScanner(); + return rv; +} + +void +CSSParserImpl::ParseLonghandProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands, + "ParseLonghandProperty must only take a longhand property"); + + RefPtr declaration = new css::Declaration; + declaration->InitializeEmpty(); + + bool changed; + ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal, + declaration, &changed, + /* aIsImportant */ false, + /* aIsSVGMode */ false); + + if (changed) { + aValue = *declaration->GetNormalBlock()->ValueFor(aPropID); + } else { + aValue.Reset(); + } +} + +bool +CSSParserImpl::ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aValue) +{ + RefPtr declaration = new css::Declaration(); + declaration->InitializeEmpty(); + + mData.AssertInitialState(); + mTempData.AssertInitialState(); + + nsCSSScanner scanner(aPropValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr); + InitScanner(scanner, reporter, nullptr, nullptr, nullptr); + + bool parsedOK = ParseTransform(false, aDisallowRelativeValues); + // We should now be at EOF + if (parsedOK && GetToken(true)) { + parsedOK = false; + } + + bool changed = false; + if (parsedOK) { + declaration->ExpandTo(&mData); + changed = mData.TransferFromBlock(mTempData, eCSSProperty_transform, + EnabledState(), false, + true, false, declaration, + GetDocument()); + declaration->CompressFrom(&mData); + } + + if (changed) { + aValue = *declaration->GetNormalBlock()->ValueFor(eCSSProperty_transform); + } else { + aValue.Reset(); + } + + ReleaseScanner(); + + return parsedOK; +} + +void +CSSParserImpl::ParseProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant, + bool aIsSVGMode) +{ + NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); + NS_PRECONDITION(aBaseURI, "need base URI"); + NS_PRECONDITION(aDeclaration, "Need declaration to parse into!"); + MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable); + + mData.AssertInitialState(); + mTempData.AssertInitialState(); + aDeclaration->AssertMutable(); + + nsCSSScanner scanner(aPropValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); + InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); + mSection = eCSSSection_General; + scanner.SetSVGMode(aIsSVGMode); + + *aChanged = false; + + // Check for unknown or preffed off properties + if (eCSSProperty_UNKNOWN == aPropID || + !nsCSSProps::IsEnabled(aPropID, EnabledState())) { + NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID)); + REPORT_UNEXPECTED_P(PEUnknownProperty, propName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + ReleaseScanner(); + return; + } + + bool parsedOK = ParseProperty(aPropID); + // We should now be at EOF + if (parsedOK && GetToken(true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + parsedOK = false; + } + + if (!parsedOK) { + NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID)); + REPORT_UNEXPECTED_P(PEValueParsingError, propName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + mTempData.ClearProperty(aPropID); + } else { + + // We know we don't need to force a ValueAppended call for the new + // value. So if we are not processing a shorthand, and there's + // already a value for this property in the declaration at the + // same importance level, then we can just copy our parsed value + // directly into the declaration without going through the whole + // expand/compress thing. + if (!aDeclaration->TryReplaceValue(aPropID, aIsImportant, mTempData, + aChanged)) { + // Do it the slow way + aDeclaration->ExpandTo(&mData); + *aChanged = mData.TransferFromBlock(mTempData, aPropID, + EnabledState(), aIsImportant, + true, false, aDeclaration, + GetDocument()); + aDeclaration->CompressFrom(&mData); + } + CLEAR_ERROR(); + } + + mTempData.AssertInitialState(); + + ReleaseScanner(); +} + +void +CSSParserImpl::ParseVariable(const nsAString& aVariableName, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant) +{ + NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); + NS_PRECONDITION(aBaseURI, "need base URI"); + NS_PRECONDITION(aDeclaration, "Need declaration to parse into!"); + NS_PRECONDITION(nsLayoutUtils::CSSVariablesEnabled(), + "expected Variables to be enabled"); + + mData.AssertInitialState(); + mTempData.AssertInitialState(); + aDeclaration->AssertMutable(); + + nsCSSScanner scanner(aPropValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURI); + InitScanner(scanner, reporter, aSheetURI, aBaseURI, aSheetPrincipal); + mSection = eCSSSection_General; + + *aChanged = false; + + CSSVariableDeclarations::Type variableType; + nsString variableValue; + + bool parsedOK = ParseVariableDeclaration(&variableType, variableValue); + + // We should now be at EOF + if (parsedOK && GetToken(true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + parsedOK = false; + } + + if (!parsedOK) { + REPORT_UNEXPECTED_P(PEValueParsingError, NS_LITERAL_STRING("--") + + aVariableName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + } else { + CLEAR_ERROR(); + aDeclaration->AddVariable(aVariableName, variableType, + variableValue, aIsImportant, true); + *aChanged = true; + } + + mTempData.AssertInitialState(); + + ReleaseScanner(); +} + +void +CSSParserImpl::ParseMediaList(const nsSubstring& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + nsMediaList* aMediaList, + bool aHTMLMode) +{ + // XXX Are there cases where the caller wants to keep what it already + // has in case of parser error? If GatherMedia ever changes to return + // a value other than true, we probably should avoid modifying aMediaList. + aMediaList->Clear(); + + // fake base URI since media lists don't have URIs in them + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + mHTMLMediaMode = aHTMLMode; + + // XXXldb We need to make the scanner not skip CSS comments! (Or + // should we?) + + // For aHTMLMode, we used to follow the parsing rules in + // http://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-media-descriptors + // which wouldn't work for media queries since they remove all but the + // first word. However, they're changed in + // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-document.html#media2 + // (as of 2008-05-29) which says that the media attribute just points + // to a media query. (The main substative difference is the relative + // precedence of commas and paretheses.) + + DebugOnly parsedOK = GatherMedia(aMediaList, false); + NS_ASSERTION(parsedOK, "GatherMedia returned false; we probably want to avoid " + "trashing aMediaList"); + + CLEAR_ERROR(); + ReleaseScanner(); + mHTMLMediaMode = false; +} + +// = #? +// = ? +bool +CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + InfallibleTArray< nsAutoPtr >& aQueries, + InfallibleTArray& aValues, + bool aHTMLMode) +{ + aQueries.Clear(); + aValues.Clear(); + + // fake base URI since media value lists don't have URIs in them + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + // See ParseMediaList comment about HTML mode + mHTMLMediaMode = aHTMLMode; + + // https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute + bool hitEnd = false; + do { + bool hitError = false; + // Parse single + do { + nsAutoPtr query; + nsCSSValue value; + + bool hitStop; + if (!ParseMediaQuery(eMediaQuerySingleCondition, getter_Transfers(query), + &hitStop)) { + NS_ASSERTION(!hitStop, "should return true when hit stop"); + hitError = true; + break; + } + + if (!query) { + REPORT_UNEXPECTED_EOF(PEParseSourceSizeListEOF); + NS_ASSERTION(hitStop, + "should return hitStop or an error if returning no query"); + hitError = true; + break; + } + + if (hitStop) { + // Empty conditions (e.g. just a bare value) should be treated as always + // matching (a query with no expressions fails to match, so a negated one + // always matches.) + query->SetNegated(); + } + + // https://html.spec.whatwg.org/multipage/embedded-content.html#source-size-value + // Percentages are not allowed in a , to avoid + // confusion about what it would be relative to. + if (ParseNonNegativeVariant(value, VARIANT_LCALC, nullptr) != + CSSParseResult::Ok) { + hitError = true; + break; + } + + if (GetToken(true)) { + if (!mToken.IsSymbol(',')) { + REPORT_UNEXPECTED_TOKEN(PEParseSourceSizeListNotComma); + hitError = true; + break; + } + } else { + hitEnd = true; + } + + aQueries.AppendElement(query.forget()); + aValues.AppendElement(value); + } while(0); + + if (hitError) { + OUTPUT_ERROR(); + + // Per spec, we just skip the current entry if there was a parse error. + // Jumps to next entry of which is a comma-separated list. + if (!SkipUntil(',')) { + hitEnd = true; + } + } + } while (!hitEnd); + + CLEAR_ERROR(); + ReleaseScanner(); + mHTMLMediaMode = false; + + return !aQueries.IsEmpty(); +} + +bool +CSSParserImpl::ParseColorString(const nsSubstring& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue, + bool aSuppressErrors /* false */) +{ + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + nsAutoSuppressErrors suppressErrors(this, aSuppressErrors); + + // Parse a color, and check that there's nothing else after it. + bool colorParsed = ParseColor(aValue) == CSSParseResult::Ok && + !GetToken(true); + + if (aSuppressErrors) { + CLEAR_ERROR(); + } else { + OUTPUT_ERROR(); + } + + ReleaseScanner(); + return colorParsed; +} + +bool +CSSParserImpl::ParseMarginString(const nsSubstring& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue, + bool aSuppressErrors /* false */) +{ + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + nsAutoSuppressErrors suppressErrors(this, aSuppressErrors); + + // Parse a margin, and check that there's nothing else after it. + bool marginParsed = ParseGroupedBoxProperty(VARIANT_LP, aValue, 0) && !GetToken(true); + + if (aSuppressErrors) { + CLEAR_ERROR(); + } else { + OUTPUT_ERROR(); + } + + ReleaseScanner(); + return marginParsed; +} + +bool +CSSParserImpl::ParseFontFamilyListString(const nsSubstring& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSValue& aValue) +{ + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + // Parse a font family list, and check that there's nothing else after it. + bool familyParsed = ParseFamily(aValue) && !GetToken(true); + OUTPUT_ERROR(); + ReleaseScanner(); + return familyParsed; +} + +nsresult +CSSParserImpl::ParseSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + nsCSSSelectorList **aSelectorList) +{ + nsCSSScanner scanner(aSelectorString, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + bool success = ParseSelectorList(*aSelectorList, char16_t(0)); + + // We deliberately do not call OUTPUT_ERROR here, because all our + // callers map a failure return to a JS exception, and if that JS + // exception is caught, people don't want to see parser diagnostics; + // see e.g. http://bugs.jquery.com/ticket/7535 + // It would be nice to be able to save the parser diagnostics into + // the exception, so that if it _isn't_ caught we can report them + // along with the usual uncaught-exception message, but we don't + // have any way to do that at present; see bug 631621. + CLEAR_ERROR(); + ReleaseScanner(); + + if (success) { + NS_ASSERTION(*aSelectorList, "Should have list!"); + return NS_OK; + } + + NS_ASSERTION(!*aSelectorList, "Shouldn't have list!"); + + return NS_ERROR_DOM_SYNTAX_ERR; +} + + +already_AddRefed +CSSParserImpl::ParseKeyframeRule(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber) +{ + nsCSSScanner scanner(aBuffer, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + RefPtr result = ParseKeyframeRule(); + if (GetToken(true)) { + // extra garbage at the end + result = nullptr; + } + + OUTPUT_ERROR(); + ReleaseScanner(); + + return result.forget(); +} + +bool +CSSParserImpl::ParseKeyframeSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + InfallibleTArray& aSelectorList) +{ + MOZ_ASSERT(aSelectorList.IsEmpty(), "given list should start empty"); + + nsCSSScanner scanner(aSelectorString, aLineNumber); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURI); + InitScanner(scanner, reporter, aURI, aURI, nullptr); + + bool success = ParseKeyframeSelectorList(aSelectorList) && + // must consume entire input string + !GetToken(true); + + OUTPUT_ERROR(); + ReleaseScanner(); + + if (success) { + NS_ASSERTION(!aSelectorList.IsEmpty(), "should not be empty"); + } else { + aSelectorList.Clear(); + } + + return success; +} + +bool +CSSParserImpl::EvaluateSupportsDeclaration(const nsAString& aProperty, + const nsAString& aValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal) +{ + nsCSSPropertyID propID = LookupEnabledProperty(aProperty); + if (propID == eCSSProperty_UNKNOWN) { + return false; + } + + nsCSSScanner scanner(aValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL); + InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); + nsAutoSuppressErrors suppressErrors(this); + + bool parsedOK; + + if (propID == eCSSPropertyExtra_variable) { + MOZ_ASSERT(Substring(aProperty, 0, + CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); + const nsDependentSubstring varName = + Substring(aProperty, CSS_CUSTOM_NAME_PREFIX_LENGTH); // remove '--' + CSSVariableDeclarations::Type variableType; + nsString variableValue; + parsedOK = ParseVariableDeclaration(&variableType, variableValue) && + !GetToken(true); + } else { + parsedOK = ParseProperty(propID) && !GetToken(true); + + mTempData.ClearProperty(propID); + mTempData.AssertInitialState(); + } + + CLEAR_ERROR(); + ReleaseScanner(); + + return parsedOK; +} + +bool +CSSParserImpl::EvaluateSupportsCondition(const nsAString& aDeclaration, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal) +{ + nsCSSScanner scanner(aDeclaration, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURL); + InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); + nsAutoSuppressErrors suppressErrors(this); + + bool conditionMet; + bool parsedOK = ParseSupportsCondition(conditionMet) && !GetToken(true); + + CLEAR_ERROR(); + ReleaseScanner(); + + return parsedOK && conditionMet; +} + +bool +CSSParserImpl::EnumerateVariableReferences(const nsAString& aPropertyValue, + VariableEnumFunc aFunc, + void* aData) +{ + nsCSSScanner scanner(aPropertyValue, 0); + css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr); + InitScanner(scanner, reporter, nullptr, nullptr, nullptr); + nsAutoSuppressErrors suppressErrors(this); + + CSSVariableDeclarations::Type type; + bool dropBackslash; + nsString impliedCharacters; + bool result = ParseValueWithVariables(&type, &dropBackslash, + impliedCharacters, aFunc, aData) && + !GetToken(true); + + ReleaseScanner(); + + return result; +} + +static bool +SeparatorRequiredBetweenTokens(nsCSSTokenSerializationType aToken1, + nsCSSTokenSerializationType aToken2) +{ + // The two lines marked with (*) do not correspond to entries in + // the table in the css-syntax spec but which we need to handle, + // as we treat them as whole tokens. + switch (aToken1) { + case eCSSTokenSerialization_Ident: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_URL_or_BadURL || + aToken2 == eCSSTokenSerialization_Symbol_Minus || + aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension || + aToken2 == eCSSTokenSerialization_URange || + aToken2 == eCSSTokenSerialization_CDC || + aToken2 == eCSSTokenSerialization_Symbol_OpenParen; + case eCSSTokenSerialization_AtKeyword_or_Hash: + case eCSSTokenSerialization_Dimension: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_URL_or_BadURL || + aToken2 == eCSSTokenSerialization_Symbol_Minus || + aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension || + aToken2 == eCSSTokenSerialization_URange || + aToken2 == eCSSTokenSerialization_CDC; + case eCSSTokenSerialization_Symbol_Hash: + case eCSSTokenSerialization_Symbol_Minus: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_URL_or_BadURL || + aToken2 == eCSSTokenSerialization_Symbol_Minus || + aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension || + aToken2 == eCSSTokenSerialization_URange; + case eCSSTokenSerialization_Number: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_URL_or_BadURL || + aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension || + aToken2 == eCSSTokenSerialization_URange; + case eCSSTokenSerialization_Symbol_At: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_URL_or_BadURL || + aToken2 == eCSSTokenSerialization_Symbol_Minus || + aToken2 == eCSSTokenSerialization_URange; + case eCSSTokenSerialization_URange: + return aToken2 == eCSSTokenSerialization_Ident || + aToken2 == eCSSTokenSerialization_Function || + aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension || + aToken2 == eCSSTokenSerialization_Symbol_Question; + case eCSSTokenSerialization_Symbol_Dot_or_Plus: + return aToken2 == eCSSTokenSerialization_Number || + aToken2 == eCSSTokenSerialization_Percentage || + aToken2 == eCSSTokenSerialization_Dimension; + case eCSSTokenSerialization_Symbol_Assorted: + case eCSSTokenSerialization_Symbol_Asterisk: + return aToken2 == eCSSTokenSerialization_Symbol_Equals; + case eCSSTokenSerialization_Symbol_Bar: + return aToken2 == eCSSTokenSerialization_Symbol_Equals || + aToken2 == eCSSTokenSerialization_Symbol_Bar || + aToken2 == eCSSTokenSerialization_DashMatch; // (*) + case eCSSTokenSerialization_Symbol_Slash: + return aToken2 == eCSSTokenSerialization_Symbol_Asterisk || + aToken2 == eCSSTokenSerialization_ContainsMatch; // (*) + default: + MOZ_ASSERT(aToken1 == eCSSTokenSerialization_Nothing || + aToken1 == eCSSTokenSerialization_Whitespace || + aToken1 == eCSSTokenSerialization_Percentage || + aToken1 == eCSSTokenSerialization_URL_or_BadURL || + aToken1 == eCSSTokenSerialization_Function || + aToken1 == eCSSTokenSerialization_CDC || + aToken1 == eCSSTokenSerialization_Symbol_OpenParen || + aToken1 == eCSSTokenSerialization_Symbol_Question || + aToken1 == eCSSTokenSerialization_Symbol_Assorted || + aToken1 == eCSSTokenSerialization_Symbol_Asterisk || + aToken1 == eCSSTokenSerialization_Symbol_Equals || + aToken1 == eCSSTokenSerialization_Symbol_Bar || + aToken1 == eCSSTokenSerialization_Symbol_Slash || + aToken1 == eCSSTokenSerialization_Other, + "unexpected nsCSSTokenSerializationType value"); + return false; + } +} + +/** + * Appends aValue to aResult, possibly inserting an empty CSS + * comment between the two to ensure that tokens from both strings + * remain separated. + */ +static void +AppendTokens(nsAString& aResult, + nsCSSTokenSerializationType& aResultFirstToken, + nsCSSTokenSerializationType& aResultLastToken, + nsCSSTokenSerializationType aValueFirstToken, + nsCSSTokenSerializationType aValueLastToken, + const nsAString& aValue) +{ + if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) { + aResult.AppendLiteral("/**/"); + } + aResult.Append(aValue); + if (aResultFirstToken == eCSSTokenSerialization_Nothing) { + aResultFirstToken = aValueFirstToken; + } + if (aValueLastToken != eCSSTokenSerialization_Nothing) { + aResultLastToken = aValueLastToken; + } +} + +/** + * Stops the given scanner recording, and appends the recorded result + * to aResult, possibly inserting an empty CSS comment between the two to + * ensure that tokens from both strings remain separated. + */ +static void +StopRecordingAndAppendTokens(nsString& aResult, + nsCSSTokenSerializationType& aResultFirstToken, + nsCSSTokenSerializationType& aResultLastToken, + nsCSSTokenSerializationType aValueFirstToken, + nsCSSTokenSerializationType aValueLastToken, + nsCSSScanner* aScanner) +{ + if (SeparatorRequiredBetweenTokens(aResultLastToken, aValueFirstToken)) { + aResult.AppendLiteral("/**/"); + } + aScanner->StopRecording(aResult); + if (aResultFirstToken == eCSSTokenSerialization_Nothing) { + aResultFirstToken = aValueFirstToken; + } + if (aValueLastToken != eCSSTokenSerialization_Nothing) { + aResultLastToken = aValueLastToken; + } +} + +bool +CSSParserImpl::ResolveValueWithVariableReferencesRec( + nsString& aResult, + nsCSSTokenSerializationType& aResultFirstToken, + nsCSSTokenSerializationType& aResultLastToken, + const CSSVariableValues* aVariables) +{ + // This function assumes we are already recording, and will leave the scanner + // recording when it returns. + MOZ_ASSERT(mScanner->IsRecording()); + MOZ_ASSERT(aResult.IsEmpty()); + + // Stack of closing characters for currently open constructs. + AutoTArray stack; + + // The resolved value for this ResolveValueWithVariableReferencesRec call. + nsString value; + + // The length of the scanner's recording before the currently parsed token. + // This is used so that when we encounter a "var(" token, we can strip + // it off the end of the recording, regardless of how long the token was. + // (With escapes, it could be longer than four characters.) + uint32_t lengthBeforeVar = 0; + + // Tracking the type of token that appears at the start and end of |value| + // and that appears at the start and end of the scanner recording. These are + // used to determine whether we need to insert "/**/" when pasting token + // streams together. + nsCSSTokenSerializationType valueFirstToken = eCSSTokenSerialization_Nothing, + valueLastToken = eCSSTokenSerialization_Nothing, + recFirstToken = eCSSTokenSerialization_Nothing, + recLastToken = eCSSTokenSerialization_Nothing; + +#define UPDATE_RECORDING_TOKENS(type) \ + if (recFirstToken == eCSSTokenSerialization_Nothing) { \ + recFirstToken = type; \ + } \ + recLastToken = type; + + while (GetToken(false)) { + switch (mToken.mType) { + case eCSSToken_Symbol: { + nsCSSTokenSerializationType type = eCSSTokenSerialization_Other; + if (mToken.mSymbol == '(') { + stack.AppendElement(')'); + type = eCSSTokenSerialization_Symbol_OpenParen; + } else if (mToken.mSymbol == '[') { + stack.AppendElement(']'); + } else if (mToken.mSymbol == '{') { + stack.AppendElement('}'); + } else if (mToken.mSymbol == ';') { + if (stack.IsEmpty()) { + // A ';' that is at the top level of the value or at the top level + // of a variable reference's fallback is invalid. + return false; + } + } else if (mToken.mSymbol == '!') { + if (stack.IsEmpty()) { + // An '!' that is at the top level of the value or at the top level + // of a variable reference's fallback is invalid. + return false; + } + } else if (mToken.mSymbol == ')' && + stack.IsEmpty()) { + // We're closing a "var(". + nsString finalTokens; + mScanner->StopRecording(finalTokens); + MOZ_ASSERT(finalTokens[finalTokens.Length() - 1] == ')'); + finalTokens.Truncate(finalTokens.Length() - 1); + aResult.Append(value); + + AppendTokens(aResult, valueFirstToken, valueLastToken, + recFirstToken, recLastToken, finalTokens); + + mScanner->StartRecording(); + UngetToken(); + aResultFirstToken = valueFirstToken; + aResultLastToken = valueLastToken; + return true; + } else if (mToken.mSymbol == ')' || + mToken.mSymbol == ']' || + mToken.mSymbol == '}') { + if (stack.IsEmpty() || + stack.LastElement() != mToken.mSymbol) { + // A mismatched closing bracket is invalid. + return false; + } + stack.TruncateLength(stack.Length() - 1); + } else if (mToken.mSymbol == '#') { + type = eCSSTokenSerialization_Symbol_Hash; + } else if (mToken.mSymbol == '@') { + type = eCSSTokenSerialization_Symbol_At; + } else if (mToken.mSymbol == '.' || + mToken.mSymbol == '+') { + type = eCSSTokenSerialization_Symbol_Dot_or_Plus; + } else if (mToken.mSymbol == '-') { + type = eCSSTokenSerialization_Symbol_Minus; + } else if (mToken.mSymbol == '?') { + type = eCSSTokenSerialization_Symbol_Question; + } else if (mToken.mSymbol == '$' || + mToken.mSymbol == '^' || + mToken.mSymbol == '~') { + type = eCSSTokenSerialization_Symbol_Assorted; + } else if (mToken.mSymbol == '=') { + type = eCSSTokenSerialization_Symbol_Equals; + } else if (mToken.mSymbol == '|') { + type = eCSSTokenSerialization_Symbol_Bar; + } else if (mToken.mSymbol == '/') { + type = eCSSTokenSerialization_Symbol_Slash; + } else if (mToken.mSymbol == '*') { + type = eCSSTokenSerialization_Symbol_Asterisk; + } + UPDATE_RECORDING_TOKENS(type); + break; + } + + case eCSSToken_Function: + if (mToken.mIdent.LowerCaseEqualsLiteral("var")) { + // Save the tokens before the "var(" to our resolved value. + nsString recording; + mScanner->StopRecording(recording); + recording.Truncate(lengthBeforeVar); + AppendTokens(value, valueFirstToken, valueLastToken, + recFirstToken, recLastToken, recording); + recFirstToken = eCSSTokenSerialization_Nothing; + recLastToken = eCSSTokenSerialization_Nothing; + + if (!GetToken(true) || + mToken.mType != eCSSToken_Ident || + !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) { + // "var(" must be followed by an identifier, and it must be a + // custom property name. + return false; + } + + // Turn the custom property name into a variable name by removing the + // '--' prefix. + MOZ_ASSERT(Substring(mToken.mIdent, 0, + CSS_CUSTOM_NAME_PREFIX_LENGTH). + EqualsLiteral("--")); + nsDependentString variableName(mToken.mIdent, + CSS_CUSTOM_NAME_PREFIX_LENGTH); + + // Get the value of the identified variable. Note that we + // check if the variable value is the empty string, as that means + // that the variable was invalid at computed value time due to + // unresolveable variable references or cycles. + nsString variableValue; + nsCSSTokenSerializationType varFirstToken, varLastToken; + bool valid = aVariables->Get(variableName, variableValue, + varFirstToken, varLastToken) && + !variableValue.IsEmpty(); + + if (!GetToken(true) || + mToken.IsSymbol(')')) { + mScanner->StartRecording(); + if (!valid) { + // Invalid variable with no fallback. + return false; + } + // Valid variable with no fallback. + AppendTokens(value, valueFirstToken, valueLastToken, + varFirstToken, varLastToken, variableValue); + } else if (mToken.IsSymbol(',')) { + mScanner->StartRecording(); + if (!GetToken(false) || + mToken.IsSymbol(')')) { + // Comma must be followed by at least one fallback token. + return false; + } + UngetToken(); + if (valid) { + // Valid variable with ignored fallback. + mScanner->StopRecording(); + AppendTokens(value, valueFirstToken, valueLastToken, + varFirstToken, varLastToken, variableValue); + bool ok = SkipBalancedContentUntil(')'); + mScanner->StartRecording(); + if (!ok) { + return false; + } + } else { + nsString fallback; + if (!ResolveValueWithVariableReferencesRec(fallback, + varFirstToken, + varLastToken, + aVariables)) { + // Fallback value had invalid tokens or an invalid variable reference + // that itself had no fallback. + return false; + } + AppendTokens(value, valueFirstToken, valueLastToken, + varFirstToken, varLastToken, fallback); + // Now we're either at the pushed back ')' that finished the + // fallback or at EOF. + DebugOnly gotToken = GetToken(false); + MOZ_ASSERT(!gotToken || mToken.IsSymbol(')')); + } + } else { + // Expected ',' or ')' after the variable name. + mScanner->StartRecording(); + return false; + } + } else { + stack.AppendElement(')'); + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Function); + } + break; + + case eCSSToken_Bad_String: + case eCSSToken_Bad_URL: + return false; + + case eCSSToken_Whitespace: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Whitespace); + break; + + case eCSSToken_AtKeyword: + case eCSSToken_Hash: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_AtKeyword_or_Hash); + break; + + case eCSSToken_Number: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Number); + break; + + case eCSSToken_Dimension: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Dimension); + break; + + case eCSSToken_Ident: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Ident); + break; + + case eCSSToken_Percentage: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Percentage); + break; + + case eCSSToken_URange: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URange); + break; + + case eCSSToken_URL: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_URL_or_BadURL); + break; + + case eCSSToken_HTMLComment: + if (mToken.mIdent[0] == '-') { + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_CDC); + } else { + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other); + } + break; + + case eCSSToken_Dashmatch: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_DashMatch); + break; + + case eCSSToken_Containsmatch: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_ContainsMatch); + break; + + default: + MOZ_FALLTHROUGH_ASSERT("unexpected token type"); + case eCSSToken_ID: + case eCSSToken_String: + case eCSSToken_Includes: + case eCSSToken_Beginsmatch: + case eCSSToken_Endsmatch: + UPDATE_RECORDING_TOKENS(eCSSTokenSerialization_Other); + break; + } + + lengthBeforeVar = mScanner->RecordingLength(); + } + +#undef UPDATE_RECORDING_TOKENS + + aResult.Append(value); + StopRecordingAndAppendTokens(aResult, valueFirstToken, valueLastToken, + recFirstToken, recLastToken, mScanner); + + // Append any implicitly closed brackets. + if (!stack.IsEmpty()) { + do { + aResult.Append(stack.LastElement()); + stack.TruncateLength(stack.Length() - 1); + } while (!stack.IsEmpty()); + valueLastToken = eCSSTokenSerialization_Other; + } + + mScanner->StartRecording(); + aResultFirstToken = valueFirstToken; + aResultLastToken = valueLastToken; + return true; +} + +bool +CSSParserImpl::ResolveValueWithVariableReferences( + const CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken) +{ + aResult.Truncate(0); + + // Start recording before we read the first token. + mScanner->StartRecording(); + + if (!GetToken(false)) { + // Value was empty since we reached EOF. + mScanner->StopRecording(); + return false; + } + + UngetToken(); + + nsString value; + nsCSSTokenSerializationType firstToken, lastToken; + bool ok = ResolveValueWithVariableReferencesRec(value, firstToken, lastToken, aVariables) && + !GetToken(true); + + mScanner->StopRecording(); + + if (ok) { + aResult = value; + aFirstToken = firstToken; + aLastToken = lastToken; + } + return ok; +} + +bool +CSSParserImpl::ResolveVariableValue(const nsAString& aPropertyValue, + const CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken) +{ + nsCSSScanner scanner(aPropertyValue, 0); + + // At this point, we know that aPropertyValue is syntactically correct + // for a token stream that has variable references. We also won't be + // interpreting any of the stream as we parse it, apart from expanding + // var() references, so we don't need a base URL etc. or any useful + // error reporting. + css::ErrorReporter reporter(scanner, nullptr, nullptr, nullptr); + InitScanner(scanner, reporter, nullptr, nullptr, nullptr); + + bool valid = ResolveValueWithVariableReferences(aVariables, aResult, + aFirstToken, aLastToken); + + ReleaseScanner(); + return valid; +} + +void +CSSParserImpl::ParsePropertyWithVariableReferences( + nsCSSPropertyID aPropertyID, + nsCSSPropertyID aShorthandPropertyID, + const nsAString& aValue, + const CSSVariableValues* aVariables, + nsRuleData* aRuleData, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal, + CSSStyleSheet* aSheet, + uint32_t aLineNumber, + uint32_t aLineOffset) +{ + mTempData.AssertInitialState(); + + bool valid; + nsString expandedValue; + + // Resolve any variable references in the property value. + { + nsCSSScanner scanner(aValue, 0); + css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL); + InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); + + nsCSSTokenSerializationType firstToken, lastToken; + valid = ResolveValueWithVariableReferences(aVariables, expandedValue, + firstToken, lastToken); + if (!valid) { + NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropertyID)); + REPORT_UNEXPECTED(PEInvalidVariableReference); + REPORT_UNEXPECTED_P(PEValueParsingError, propName); + if (nsCSSProps::IsInherited(aPropertyID)) { + REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit); + } else { + REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial); + } + OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset); + } + ReleaseScanner(); + } + + nsCSSPropertyID propertyToParse = + aShorthandPropertyID != eCSSProperty_UNKNOWN ? aShorthandPropertyID : + aPropertyID; + + // Parse the property with that resolved value. + if (valid) { + nsCSSScanner scanner(expandedValue, 0); + css::ErrorReporter reporter(scanner, aSheet, mChildLoader, aDocURL); + InitScanner(scanner, reporter, aDocURL, aBaseURL, aDocPrincipal); + valid = ParseProperty(propertyToParse); + if (valid && GetToken(true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + valid = false; + } + if (!valid) { + NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue( + propertyToParse)); + REPORT_UNEXPECTED_P_V(PEValueWithVariablesParsingErrorInValue, + propName, expandedValue); + if (nsCSSProps::IsInherited(aPropertyID)) { + REPORT_UNEXPECTED(PEValueWithVariablesFallbackInherit); + } else { + REPORT_UNEXPECTED(PEValueWithVariablesFallbackInitial); + } + OUTPUT_ERROR_WITH_POSITION(aLineNumber, aLineOffset); + } + ReleaseScanner(); + } + + // If the property could not be parsed with the resolved value, then we + // treat it as if the value were 'initial' or 'inherit', depending on whether + // the property is an inherited property. + if (!valid) { + nsCSSValue defaultValue; + if (nsCSSProps::IsInherited(aPropertyID)) { + defaultValue.SetInheritValue(); + } else { + defaultValue.SetInitialValue(); + } + mTempData.AddLonghandProperty(aPropertyID, defaultValue); + } + + // Copy the property value into the rule data. + mTempData.MapRuleInfoInto(aPropertyID, aRuleData); + + mTempData.ClearProperty(propertyToParse); + mTempData.AssertInitialState(); +} + +bool +CSSParserImpl::ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL, + nsAString& aName) +{ + nsCSSScanner scanner(aBuffer, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aURL); + InitScanner(scanner, reporter, aURL, aURL, nullptr); + + bool success = ParseCounterStyleName(aName, true) && !GetToken(true); + + OUTPUT_ERROR(); + ReleaseScanner(); + + return success; +} + +bool +CSSParserImpl::ParseCounterDescriptor(nsCSSCounterDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + nsCSSScanner scanner(aBuffer, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL); + InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal); + + bool success = ParseCounterDescriptorValue(aDescID, aValue) && + !GetToken(true); + + OUTPUT_ERROR(); + ReleaseScanner(); + + return success; +} + +bool +CSSParserImpl::ParseFontFaceDescriptor(nsCSSFontDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + nsCSSScanner scanner(aBuffer, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL); + InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal); + + bool success = ParseFontDescriptorValue(aDescID, aValue) && + !GetToken(true); + + OUTPUT_ERROR(); + ReleaseScanner(); + + return success; +} + +//---------------------------------------------------------------------- + +bool +CSSParserImpl::GetToken(bool aSkipWS) +{ + if (mHavePushBack) { + mHavePushBack = false; + if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) { + return true; + } + } + return mScanner->Next(mToken, aSkipWS ? + eCSSScannerExclude_WhitespaceAndComments : + eCSSScannerExclude_Comments); +} + +void +CSSParserImpl::UngetToken() +{ + NS_PRECONDITION(!mHavePushBack, "double pushback"); + mHavePushBack = true; +} + +bool +CSSParserImpl::GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum) +{ + // Peek at next token so that mScanner updates line and column vals + if (!GetToken(aSkipWS)) { + return false; + } + UngetToken(); + // The scanner uses one-indexing for line numbers but zero-indexing + // for column numbers. + *linenum = mScanner->GetLineNumber(); + *colnum = 1 + mScanner->GetColumnNumber(); + return true; +} + +bool +CSSParserImpl::ExpectSymbol(char16_t aSymbol, + bool aSkipWS) +{ + if (!GetToken(aSkipWS)) { + // CSS2.1 specifies that all "open constructs" are to be closed at + // EOF. It simplifies higher layers if we claim to have found an + // ), ], }, or ; if we encounter EOF while looking for one of them. + // Do still issue a diagnostic, to aid debugging. + if (aSymbol == ')' || aSymbol == ']' || + aSymbol == '}' || aSymbol == ';') { + REPORT_UNEXPECTED_EOF_CHAR(aSymbol); + return true; + } + else + return false; + } + if (mToken.IsSymbol(aSymbol)) { + return true; + } + UngetToken(); + return false; +} + +// Checks to see if we're at the end of a property. If an error occurs during +// the check, does not signal a parse error. +bool +CSSParserImpl::CheckEndProperty() +{ + if (!GetToken(true)) { + return true; // properties may end with eof + } + if ((eCSSToken_Symbol == mToken.mType) && + ((';' == mToken.mSymbol) || + ('!' == mToken.mSymbol) || + ('}' == mToken.mSymbol) || + (')' == mToken.mSymbol))) { + // XXX need to verify that ! is only followed by "important [;|}] + // XXX this requires a multi-token pushback buffer + UngetToken(); + return true; + } + UngetToken(); + return false; +} + +// Checks if we're at the end of a property, raising an error if we're not. +bool +CSSParserImpl::ExpectEndProperty() +{ + if (CheckEndProperty()) + return true; + + // If we're here, we read something incorrect, so we should report it. + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + return false; +} + +// Parses the priority suffix on a property, which at present may be +// either '!important' or nothing. +CSSParserImpl::PriorityParsingStatus +CSSParserImpl::ParsePriority() +{ + if (!GetToken(true)) { + return ePriority_None; // properties may end with EOF + } + if (!mToken.IsSymbol('!')) { + UngetToken(); + return ePriority_None; // dunno what it is, but it's not a priority + } + + if (!GetToken(true)) { + // EOF is not ok after ! + REPORT_UNEXPECTED_EOF(PEImportantEOF); + return ePriority_Error; + } + + if (mToken.mType != eCSSToken_Ident || + !mToken.mIdent.LowerCaseEqualsLiteral("important")) { + REPORT_UNEXPECTED_TOKEN(PEExpectedImportant); + UngetToken(); + return ePriority_Error; + } + + return ePriority_Important; +} + +nsSubstring* +CSSParserImpl::NextIdent() +{ + // XXX Error reporting? + if (!GetToken(true)) { + return nullptr; + } + if (eCSSToken_Ident != mToken.mType) { + UngetToken(); + return nullptr; + } + return &mToken.mIdent; +} + +bool +CSSParserImpl::SkipAtRule(bool aInsideBlock) +{ + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESkipAtRuleEOF2); + return false; + } + if (eCSSToken_Symbol == mToken.mType) { + char16_t symbol = mToken.mSymbol; + if (symbol == ';') { + break; + } + if (aInsideBlock && symbol == '}') { + // The closing } doesn't belong to us. + UngetToken(); + break; + } + if (symbol == '{') { + SkipUntil('}'); + break; + } else if (symbol == '(') { + SkipUntil(')'); + } else if (symbol == '[') { + SkipUntil(']'); + } + } else if (eCSSToken_Function == mToken.mType || + eCSSToken_Bad_URL == mToken.mType) { + SkipUntil(')'); + } + } + return true; +} + +bool +CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc, + void* aData, + bool aInAtRule) +{ + + nsCSSSection newSection; + bool (CSSParserImpl::*parseFunc)(RuleAppendFunc, void*); + + if ((mSection <= eCSSSection_Charset) && + (mToken.mIdent.LowerCaseEqualsLiteral("charset"))) { + parseFunc = &CSSParserImpl::ParseCharsetRule; + newSection = eCSSSection_Import; // only one charset allowed + + } else if ((mSection <= eCSSSection_Import) && + mToken.mIdent.LowerCaseEqualsLiteral("import")) { + parseFunc = &CSSParserImpl::ParseImportRule; + newSection = eCSSSection_Import; + + } else if ((mSection <= eCSSSection_NameSpace) && + mToken.mIdent.LowerCaseEqualsLiteral("namespace")) { + parseFunc = &CSSParserImpl::ParseNameSpaceRule; + newSection = eCSSSection_NameSpace; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("media")) { + parseFunc = &CSSParserImpl::ParseMediaRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("-moz-document")) { + parseFunc = &CSSParserImpl::ParseMozDocumentRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-face")) { + parseFunc = &CSSParserImpl::ParseFontFaceRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values")) { + parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) { + parseFunc = &CSSParserImpl::ParsePageRule; + newSection = eCSSSection_General; + + } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation, + EnabledState()) && + mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) || + (nsCSSProps::IsEnabled(eCSSPropertyAlias_WebkitAnimation) && + mToken.mIdent.LowerCaseEqualsLiteral("-webkit-keyframes")) || + mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) { + parseFunc = &CSSParserImpl::ParseKeyframesRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("supports")) { + parseFunc = &CSSParserImpl::ParseSupportsRule; + newSection = eCSSSection_General; + + } else if (mToken.mIdent.LowerCaseEqualsLiteral("counter-style")) { + parseFunc = &CSSParserImpl::ParseCounterStyleRule; + newSection = eCSSSection_General; + + } else { + if (!NonMozillaVendorIdentifier(mToken.mIdent)) { + REPORT_UNEXPECTED_TOKEN(PEUnknownAtRule); + OUTPUT_ERROR(); + } + // Skip over unsupported at rule, don't advance section + return SkipAtRule(aInAtRule); + } + + // Inside of @-rules, only the rules that can occur anywhere + // are allowed. + bool unnestable = aInAtRule && newSection != eCSSSection_General; + if (unnestable) { + REPORT_UNEXPECTED_TOKEN(PEGroupRuleNestedAtRule); + } + + if (unnestable || !(this->*parseFunc)(aAppendFunc, aData)) { + // Skip over invalid at rule, don't advance section + OUTPUT_ERROR(); + return SkipAtRule(aInAtRule); + } + + // Nested @-rules don't affect the top-level rule chain requirement + if (!aInAtRule) { + mSection = newSection; + } + + return true; +} + +bool +CSSParserImpl::ParseCharsetRule(RuleAppendFunc aAppendFunc, + void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !GetToken(true)) { + REPORT_UNEXPECTED_EOF(PECharsetRuleEOF); + return false; + } + + if (eCSSToken_String != mToken.mType) { + UngetToken(); + REPORT_UNEXPECTED_TOKEN(PECharsetRuleNotString); + return false; + } + + nsAutoString charset = mToken.mIdent; + + if (!ExpectSymbol(';', true)) { + return false; + } + + // It's intentional that we don't create a rule object for @charset rules. + + return true; +} + +bool +CSSParserImpl::ParseURLOrString(nsString& aURL) +{ + if (!GetToken(true)) { + return false; + } + if (eCSSToken_String == mToken.mType || eCSSToken_URL == mToken.mType) { + aURL = mToken.mIdent; + return true; + } + UngetToken(); + return false; +} + +bool +CSSParserImpl::ParseMediaQuery(eMediaQueryType aQueryType, + nsMediaQuery **aQuery, + bool *aHitStop) +{ + *aQuery = nullptr; + *aHitStop = false; + bool inAtRule = aQueryType == eMediaQueryAtRule; + // Attempt to parse a single condition and stop + bool singleCondition = aQueryType == eMediaQuerySingleCondition; + + // "If the comma-separated list is the empty list it is assumed to + // specify the media query 'all'." (css3-mediaqueries, section + // "Media Queries") + if (!GetToken(true)) { + *aHitStop = true; + // expected termination by EOF + if (!inAtRule) + return true; + + // unexpected termination by EOF + REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); + return true; + } + + if (eCSSToken_Symbol == mToken.mType && inAtRule && + (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}' )) { + *aHitStop = true; + UngetToken(); + return true; + } + UngetToken(); + + nsMediaQuery* query = new nsMediaQuery; + *aQuery = query; + + if (ExpectSymbol('(', true)) { + // we got an expression without a media type + UngetToken(); // so ParseMediaQueryExpression can handle it + query->SetType(nsGkAtoms::all); + query->SetTypeOmitted(); + // Just parse the first expression here. + if (!ParseMediaQueryExpression(query)) { + OUTPUT_ERROR(); + query->SetHadUnknownExpression(); + } + } else if (singleCondition) { + // Since we are only trying to consume a single condition, which precludes + // media types and not/only, this should be the same as reaching immediate + // EOF (no condition to parse) + *aHitStop = true; + return true; + } else { + nsCOMPtr mediaType; + bool gotNotOrOnly = false; + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); + return false; + } + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotIdent); + UngetToken(); + return false; + } + // case insensitive from CSS - must be lower cased + nsContentUtils::ASCIIToLower(mToken.mIdent); + mediaType = NS_Atomize(mToken.mIdent); + if (!gotNotOrOnly && mediaType == nsGkAtoms::_not) { + gotNotOrOnly = true; + query->SetNegated(); + } else if (!gotNotOrOnly && mediaType == nsGkAtoms::only) { + gotNotOrOnly = true; + query->SetHasOnly(); + } else if (mediaType == nsGkAtoms::_not || + mediaType == nsGkAtoms::only || + mediaType == nsGkAtoms::_and || + mediaType == nsGkAtoms::_or) { + REPORT_UNEXPECTED_TOKEN(PEGatherMediaReservedMediaType); + UngetToken(); + return false; + } else { + // valid media type + break; + } + } + query->SetType(mediaType); + } + + for (;;) { + if (!GetToken(true)) { + *aHitStop = true; + // expected termination by EOF + if (!inAtRule) + break; + + // unexpected termination by EOF + REPORT_UNEXPECTED_EOF(PEGatherMediaEOF); + break; + } + + if (eCSSToken_Symbol == mToken.mType && inAtRule && + (mToken.mSymbol == ';' || mToken.mSymbol == '{' || mToken.mSymbol == '}')) { + *aHitStop = true; + UngetToken(); + break; + } + if (!singleCondition && + eCSSToken_Symbol == mToken.mType && mToken.mSymbol == ',') { + // Done with the expressions for this query + break; + } + if (eCSSToken_Ident != mToken.mType || + !mToken.mIdent.LowerCaseEqualsLiteral("and")) { + if (singleCondition) { + // We have a condition at this point -- if we're not chained to other + // conditions with and/or, we're done. + UngetToken(); + break; + } else { + REPORT_UNEXPECTED_TOKEN(PEGatherMediaNotComma); + UngetToken(); + return false; + } + } + if (!ParseMediaQueryExpression(query)) { + OUTPUT_ERROR(); + query->SetHadUnknownExpression(); + } + } + return true; +} + +// Returns false only when there is a low-level error in the scanner +// (out-of-memory). +bool +CSSParserImpl::GatherMedia(nsMediaList* aMedia, + bool aInAtRule) +{ + eMediaQueryType type = aInAtRule ? eMediaQueryAtRule : eMediaQueryNormal; + for (;;) { + nsAutoPtr query; + bool hitStop; + if (!ParseMediaQuery(type, getter_Transfers(query), &hitStop)) { + NS_ASSERTION(!hitStop, "should return true when hit stop"); + OUTPUT_ERROR(); + if (query) { + query->SetHadUnknownExpression(); + } + if (aInAtRule) { + const char16_t stopChars[] = + { char16_t(','), char16_t('{'), char16_t(';'), char16_t('}'), char16_t(0) }; + SkipUntilOneOf(stopChars); + } else { + SkipUntil(','); + } + // Rely on SkipUntilOneOf leaving mToken around as the last token read. + if (mToken.mType == eCSSToken_Symbol && aInAtRule && + (mToken.mSymbol == '{' || mToken.mSymbol == ';' || mToken.mSymbol == '}')) { + UngetToken(); + hitStop = true; + } + } + if (query) { + aMedia->AppendQuery(query); + } + if (hitStop) { + break; + } + } + return true; +} + +bool +CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) +{ + if (!ExpectSymbol('(', true)) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedExpressionStart); + return false; + } + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEMQExpressionEOF); + return false; + } + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); + UngetToken(); + SkipUntil(')'); + return false; + } + + nsMediaExpression *expr = aQuery->NewExpression(); + + // case insensitive from CSS - must be lower cased + nsContentUtils::ASCIIToLower(mToken.mIdent); + nsDependentString featureString(mToken.mIdent, 0); + uint8_t satisfiedReqFlags = 0; + + // Strip off "-webkit-" prefix from featureString: + if (sWebkitPrefixedAliasesEnabled && + StringBeginsWith(featureString, NS_LITERAL_STRING("-webkit-"))) { + satisfiedReqFlags |= nsMediaFeature::eHasWebkitPrefix; + featureString.Rebind(featureString, 8); + } + if (sWebkitDevicePixelRatioEnabled) { + satisfiedReqFlags |= nsMediaFeature::eWebkitDevicePixelRatioPrefEnabled; + } + + // Strip off "min-"/"max-" prefix from featureString: + if (StringBeginsWith(featureString, NS_LITERAL_STRING("min-"))) { + expr->mRange = nsMediaExpression::eMin; + featureString.Rebind(featureString, 4); + } else if (StringBeginsWith(featureString, NS_LITERAL_STRING("max-"))) { + expr->mRange = nsMediaExpression::eMax; + featureString.Rebind(featureString, 4); + } else { + expr->mRange = nsMediaExpression::eEqual; + } + + nsCOMPtr mediaFeatureAtom = NS_Atomize(featureString); + const nsMediaFeature *feature = nsMediaFeatures::features; + for (; feature->mName; ++feature) { + // See if name matches & all requirement flags are satisfied: + // (We check requirements by turning off all of the flags that have been + // satisfied, and then see if the result is 0.) + if (*(feature->mName) == mediaFeatureAtom && + !(feature->mReqFlags & ~satisfiedReqFlags)) { + break; + } + } + if (!feature->mName || + (expr->mRange != nsMediaExpression::eEqual && + feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); + SkipUntil(')'); + return false; + } + expr->mFeature = feature; + + if (!GetToken(true) || mToken.IsSymbol(')')) { + // Query expressions for any feature can be given without a value. + // However, min/max prefixes are not allowed. + if (expr->mRange != nsMediaExpression::eEqual) { + REPORT_UNEXPECTED(PEMQNoMinMaxWithoutValue); + return false; + } + expr->mValue.Reset(); + return true; + } + + if (!mToken.IsSymbol(':')) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd); + UngetToken(); + SkipUntil(')'); + return false; + } + + bool rv = false; + switch (feature->mValueType) { + case nsMediaFeature::eLength: + rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LENGTH, + nullptr); + break; + case nsMediaFeature::eInteger: + case nsMediaFeature::eBoolInteger: + rv = ParseNonNegativeInteger(expr->mValue); + // Enforce extra restrictions for eBoolInteger + if (rv && + feature->mValueType == nsMediaFeature::eBoolInteger && + expr->mValue.GetIntValue() > 1) + rv = false; + break; + case nsMediaFeature::eFloat: + rv = ParseNonNegativeNumber(expr->mValue); + break; + case nsMediaFeature::eIntRatio: + { + // Two integers separated by '/', with optional whitespace on + // either side of the '/'. + RefPtr a = nsCSSValue::Array::Create(2); + expr->mValue.SetArrayValue(a, eCSSUnit_Array); + // We don't bother with ParseNonNegativeVariant since we have to + // check for != 0 as well; no need to worry about the UngetToken + // since we're throwing out up to the next ')' anyway. + rv = ParseSingleTokenVariant(a->Item(0), VARIANT_INTEGER, nullptr) && + a->Item(0).GetIntValue() > 0 && + ExpectSymbol('/', true) && + ParseSingleTokenVariant(a->Item(1), VARIANT_INTEGER, nullptr) && + a->Item(1).GetIntValue() > 0; + } + break; + case nsMediaFeature::eResolution: + rv = GetToken(true); + if (!rv) + break; + rv = mToken.mType == eCSSToken_Dimension && mToken.mNumber > 0.0f; + if (!rv) { + UngetToken(); + break; + } + // No worries about whether unitless zero is allowed, since the + // value must be positive (and we checked that above). + NS_ASSERTION(!mToken.mIdent.IsEmpty(), "unit lied"); + if (mToken.mIdent.LowerCaseEqualsLiteral("dpi")) { + expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Inch); + } else if (mToken.mIdent.LowerCaseEqualsLiteral("dppx")) { + expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel); + } else if (mToken.mIdent.LowerCaseEqualsLiteral("dpcm")) { + expr->mValue.SetFloatValue(mToken.mNumber, eCSSUnit_Centimeter); + } else { + rv = false; + } + break; + case nsMediaFeature::eEnumerated: + rv = ParseSingleTokenVariant(expr->mValue, VARIANT_KEYWORD, + feature->mData.mKeywordTable); + break; + case nsMediaFeature::eIdent: + rv = ParseSingleTokenVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr); + break; + } + if (!rv || !ExpectSymbol(')', true)) { + REPORT_UNEXPECTED(PEMQExpectedFeatureValue); + SkipUntil(')'); + return false; + } + + return true; +} + +// Parse a CSS2 import rule: "@import STRING | URL [medium [, medium]]" +bool +CSSParserImpl::ParseImportRule(RuleAppendFunc aAppendFunc, void* aData) +{ + RefPtr media = new nsMediaList(); + + uint32_t linenum, colnum; + nsAutoString url; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !ParseURLOrString(url)) { + REPORT_UNEXPECTED_TOKEN(PEImportNotURI); + return false; + } + + if (!ExpectSymbol(';', true)) { + if (!GatherMedia(media, true) || + !ExpectSymbol(';', true)) { + REPORT_UNEXPECTED_TOKEN(PEImportUnexpected); + // don't advance section, simply ignore invalid @import + return false; + } + + // Safe to assert this, since we ensured that there is something + // other than the ';' coming after the @import's url() token. + NS_ASSERTION(media->Length() != 0, "media list must be nonempty"); + } + + ProcessImport(url, media, aAppendFunc, aData, linenum, colnum); + return true; +} + +void +CSSParserImpl::ProcessImport(const nsString& aURLSpec, + nsMediaList* aMedia, + RuleAppendFunc aAppendFunc, + void* aData, + uint32_t aLineNumber, + uint32_t aColumnNumber) +{ + RefPtr rule = new css::ImportRule(aMedia, aURLSpec, + aLineNumber, + aColumnNumber); + (*aAppendFunc)(rule, aData); + + // Diagnose bad URIs even if we don't have a child loader. + nsCOMPtr url; + // Charset will be deduced from mBaseURI, which is more or less correct. + nsresult rv = NS_NewURI(getter_AddRefs(url), aURLSpec, nullptr, mBaseURI); + + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_MALFORMED_URI) { + // import url is bad + REPORT_UNEXPECTED_P(PEImportBadURI, aURLSpec); + OUTPUT_ERROR(); + } + return; + } + + if (mChildLoader) { + mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets); + } +} + +// Parse the {} part of an @media or @-moz-document rule. +bool +CSSParserImpl::ParseGroupRule(css::GroupRule* aRule, + RuleAppendFunc aAppendFunc, + void* aData) +{ + // XXXbz this could use better error reporting throughout the method + if (!ExpectSymbol('{', true)) { + return false; + } + + // push rule on stack, loop over children + PushGroup(aRule); + nsCSSSection holdSection = mSection; + mSection = eCSSSection_General; + + for (;;) { + // Get next non-whitespace token + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEGroupRuleEOF2); + break; + } + if (mToken.IsSymbol('}')) { // done! + UngetToken(); + break; + } + if (eCSSToken_AtKeyword == mToken.mType) { + // Parse for nested rules + ParseAtRule(aAppendFunc, aData, true); + continue; + } + UngetToken(); + ParseRuleSet(AppendRuleToSheet, this, true); + } + PopGroup(); + + if (!ExpectSymbol('}', true)) { + mSection = holdSection; + return false; + } + (*aAppendFunc)(aRule, aData); + return true; +} + +// Parse a CSS2 media rule: "@media medium [, medium] { ... }" +bool +CSSParserImpl::ParseMediaRule(RuleAppendFunc aAppendFunc, void* aData) +{ + RefPtr media = new nsMediaList(); + uint32_t linenum, colnum; + if (GetNextTokenLocation(true, &linenum, &colnum) && + GatherMedia(media, true)) { + // XXXbz this could use better error reporting throughout the method + RefPtr rule = new css::MediaRule(linenum, colnum); + // Append first, so when we do SetMedia() the rule + // knows what its stylesheet is. + if (ParseGroupRule(rule, aAppendFunc, aData)) { + rule->SetMedia(media); + return true; + } + } + + return false; +} + +// Parse a @-moz-document rule. This is like an @media rule, but instead +// of a medium it has a nonempty list of items where each item is either +// url(), url-prefix(), or domain(). +bool +CSSParserImpl::ParseMozDocumentRule(RuleAppendFunc aAppendFunc, void* aData) +{ + css::DocumentRule::URL *urls = nullptr; + css::DocumentRule::URL **next = &urls; + + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum)) { + return false; + } + + do { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEMozDocRuleEOF); + delete urls; + return false; + } + + if (!(eCSSToken_URL == mToken.mType || + (eCSSToken_Function == mToken.mType && + (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix") || + mToken.mIdent.LowerCaseEqualsLiteral("domain") || + mToken.mIdent.LowerCaseEqualsLiteral("regexp"))))) { + REPORT_UNEXPECTED_TOKEN(PEMozDocRuleBadFunc2); + UngetToken(); + delete urls; + return false; + } + css::DocumentRule::URL *cur = *next = new css::DocumentRule::URL; + next = &cur->next; + if (mToken.mType == eCSSToken_URL) { + cur->func = css::DocumentRule::eURL; + CopyUTF16toUTF8(mToken.mIdent, cur->url); + } else if (mToken.mIdent.LowerCaseEqualsLiteral("regexp")) { + // regexp() is different from url-prefix() and domain() (but + // probably the way they *should* have been* in that it requires a + // string argument, and doesn't try to behave like url(). + cur->func = css::DocumentRule::eRegExp; + GetToken(true); + // copy before we know it's valid (but before ExpectSymbol changes + // mToken.mIdent) + CopyUTF16toUTF8(mToken.mIdent, cur->url); + if (eCSSToken_String != mToken.mType || !ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotString); + SkipUntil(')'); + delete urls; + return false; + } + } else { + if (mToken.mIdent.LowerCaseEqualsLiteral("url-prefix")) { + cur->func = css::DocumentRule::eURLPrefix; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("domain")) { + cur->func = css::DocumentRule::eDomain; + } + + NS_ASSERTION(!mHavePushBack, "mustn't have pushback at this point"); + mScanner->NextURL(mToken); + if (mToken.mType != eCSSToken_URL) { + REPORT_UNEXPECTED_TOKEN(PEMozDocRuleNotURI); + SkipUntil(')'); + delete urls; + return false; + } + + // We could try to make the URL (as long as it's not domain()) + // canonical and absolute with NS_NewURI and GetSpec, but I'm + // inclined to think we shouldn't. + CopyUTF16toUTF8(mToken.mIdent, cur->url); + } + } while (ExpectSymbol(',', true)); + + RefPtr rule = new css::DocumentRule(linenum, colnum); + rule->SetURLs(urls); + + return ParseGroupRule(rule, aAppendFunc, aData); +} + +// Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;" +bool +CSSParserImpl::ParseNameSpaceRule(RuleAppendFunc aAppendFunc, void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEAtNSPrefixEOF); + return false; + } + + nsAutoString prefix; + nsAutoString url; + + if (eCSSToken_Ident == mToken.mType) { + prefix = mToken.mIdent; + // user-specified identifiers are case-sensitive (bug 416106) + } else { + UngetToken(); + } + + if (!ParseURLOrString(url) || !ExpectSymbol(';', true)) { + if (mHavePushBack) { + REPORT_UNEXPECTED_TOKEN(PEAtNSUnexpected); + } else { + REPORT_UNEXPECTED_EOF(PEAtNSURIEOF); + } + return false; + } + + ProcessNameSpace(prefix, url, aAppendFunc, aData, linenum, colnum); + return true; +} + +void +CSSParserImpl::ProcessNameSpace(const nsString& aPrefix, + const nsString& aURLSpec, + RuleAppendFunc aAppendFunc, + void* aData, + uint32_t aLineNumber, + uint32_t aColumnNumber) +{ + nsCOMPtr prefix; + + if (!aPrefix.IsEmpty()) { + prefix = NS_Atomize(aPrefix); + } + + RefPtr rule = new css::NameSpaceRule(prefix, aURLSpec, + aLineNumber, + aColumnNumber); + (*aAppendFunc)(rule, aData); + + // If this was the first namespace rule encountered, it will trigger + // creation of a namespace map. + if (!mNameSpaceMap) { + mNameSpaceMap = mSheet->GetNameSpaceMap(); + } +} + +// font-face-rule: '@font-face' '{' font-description '}' +// font-description: font-descriptor+ +bool +CSSParserImpl::ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEBadFontBlockStart); + return false; + } + + RefPtr rule(new nsCSSFontFaceRule(linenum, colnum)); + + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFontFaceEOF); + break; + } + if (mToken.IsSymbol('}')) { // done! + UngetToken(); + break; + } + + // ignore extra semicolons + if (mToken.IsSymbol(';')) + continue; + + if (!ParseFontDescriptor(rule)) { + REPORT_UNEXPECTED(PEDeclSkipped); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) + break; + } + } + if (!ExpectSymbol('}', true)) { + REPORT_UNEXPECTED_TOKEN(PEBadFontBlockEnd); + return false; + } + (*aAppendFunc)(rule, aData); + return true; +} + +// font-descriptor: font-family-desc +// | font-style-desc +// | font-weight-desc +// | font-stretch-desc +// | font-src-desc +// | unicode-range-desc +// +// All font-*-desc productions follow the pattern +// IDENT ':' value ';' +// +// On entry to this function, mToken is the IDENT. + +bool +CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule) +{ + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEFontDescExpected); + return false; + } + + nsString descName = mToken.mIdent; + if (!ExpectSymbol(':', true)) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); + OUTPUT_ERROR(); + return false; + } + + nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(descName); + nsCSSValue value; + + if (descID == eCSSFontDesc_UNKNOWN || + (descID == eCSSFontDesc_Display && + !Preferences::GetBool("layout.css.font-display.enabled"))) { + if (NonMozillaVendorIdentifier(descName)) { + // silently skip other vendors' extensions + SkipDeclaration(true); + return true; + } else { + REPORT_UNEXPECTED_P(PEUnknownFontDesc, descName); + return false; + } + } + + if (!ParseFontDescriptorValue(descID, value)) { + REPORT_UNEXPECTED_P(PEValueParsingError, descName); + return false; + } + + // Expect termination by ;, }, or EOF. + if (GetToken(true)) { + if (!mToken.IsSymbol(';') && + !mToken.IsSymbol('}')) { + UngetToken(); + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + return false; + } + UngetToken(); + } + + aRule->SetDesc(descID, value); + return true; +} + +// @font-feature-values # { +// @ { +// : +; +// : +; +// ... +// } +// ... +// } + +bool +CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, + void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum)) { + return false; + } + + RefPtr + valuesRule(new nsCSSFontFeatureValuesRule(linenum, colnum)); + + // parse family list + nsCSSValue fontlistValue; + + if (!ParseFamily(fontlistValue) || + fontlistValue.GetUnit() != eCSSUnit_FontFamilyList) + { + REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily); + return false; + } + + // add family to rule + const FontFamilyList* fontlist = fontlistValue.GetFontFamilyListValue(); + + // family list has generic ==> parse error + if (fontlist->HasGeneric()) { + REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList); + return false; + } + + valuesRule->SetFamilyList(*fontlist); + + // open brace + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart); + return false; + } + + // list of sets of feature values, each set bound to a specific + // feature-type (e.g. swash, annotation) + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + break; + } + if (mToken.IsSymbol('}')) { // done! + UngetToken(); + break; + } + + if (!ParseFontFeatureValueSet(valuesRule)) { + if (!SkipAtRule(false)) { + break; + } + } + } + if (!ExpectSymbol('}', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd); + SkipUntil('}'); + return false; + } + + (*aAppendFunc)(valuesRule, aData); + return true; +} + +#define NUMVALUES_NO_LIMIT 0xFFFF + +// parse a single value set containing name-value pairs for a single feature type +// @ { [ : + ; ]* } +// Ex: @swash { flowing: 1; delicate: 2; } +bool +CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule + *aFeatureValuesRule) +{ + // -- @keyword (e.g. swash, styleset) + if (eCSSToken_AtKeyword != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt); + OUTPUT_ERROR(); + UngetToken(); + return false; + } + + // which font-specific variant of font-variant-alternates + int32_t whichVariant; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (keyword == eCSSKeyword_UNKNOWN || + !nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantAlternatesFuncsKTable, + whichVariant)) + { + if (!NonMozillaVendorIdentifier(mToken.mIdent)) { + REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue); + OUTPUT_ERROR(); + } + UngetToken(); + return false; + } + + nsAutoString featureType(mToken.mIdent); + + // open brace + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart); + return false; + } + + // styleset and character-variant can be multi-valued, otherwise single value + int32_t limitNumValues; + + switch (keyword) { + case eCSSKeyword_styleset: + limitNumValues = NUMVALUES_NO_LIMIT; + break; + case eCSSKeyword_character_variant: + limitNumValues = 2; + break; + default: + limitNumValues = 1; + break; + } + + // -- ident integer+ [, ident integer+] + AutoTArray values; + + // list of font-feature-values-declaration's + for (;;) { + nsAutoString valueId; + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + break; + } + + // ignore extra semicolons + if (mToken.IsSymbol(';')) { + continue; + } + + // close brace ==> done + if (mToken.IsSymbol('}')) { + break; + } + + // ident + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + valueId.Assign(mToken.mIdent); + + // colon + if (!ExpectSymbol(':', true)) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + // value list + AutoTArray featureSelectors; + + nsCSSValue intValue; + while (ParseNonNegativeInteger(intValue)) { + featureSelectors.AppendElement(uint32_t(intValue.GetIntValue())); + } + + int32_t numValues = featureSelectors.Length(); + + if (numValues == 0) { + REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + if (numValues > limitNumValues) { + REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); + values.AppendElement(v); + break; + } + + // ';' or '}' to end definition + if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) { + REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); + values.AppendElement(v); + + if (mToken.IsSymbol('}')) { + break; + } + } + + aFeatureValuesRule->AddValueList(whichVariant, values); + return true; +} + +bool +CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEKeyframeNameEOF); + return false; + } + + if (mToken.mType != eCSSToken_Ident) { + REPORT_UNEXPECTED_TOKEN(PEKeyframeBadName); + UngetToken(); + return false; + } + nsString name(mToken.mIdent); + + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEKeyframeBrace); + return false; + } + + RefPtr rule = new nsCSSKeyframesRule(name, + linenum, colnum); + + while (!ExpectSymbol('}', true)) { + RefPtr kid = ParseKeyframeRule(); + if (kid) { + rule->AppendStyleRule(kid); + } else { + OUTPUT_ERROR(); + SkipRuleSet(true); + } + } + + (*aAppendFunc)(rule, aData); + return true; +} + +bool +CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData) +{ + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum)) { + return false; + } + + // TODO: There can be page selectors after @page such as ":first", ":left". + uint32_t parseFlags = eParseDeclaration_InBraces | + eParseDeclaration_AllowImportant; + + // Forbid viewport units in @page rules. See bug 811391. + MOZ_ASSERT(mViewportUnitsEnabled, + "Viewport units should be enabled outside of @page rules."); + mViewportUnitsEnabled = false; + RefPtr declaration = + ParseDeclarationBlock(parseFlags, eCSSContext_Page); + mViewportUnitsEnabled = true; + + if (!declaration) { + return false; + } + + RefPtr rule = + new nsCSSPageRule(declaration, linenum, colnum); + + (*aAppendFunc)(rule, aData); + return true; +} + +already_AddRefed +CSSParserImpl::ParseKeyframeRule() +{ + InfallibleTArray selectorList; + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !ParseKeyframeSelectorList(selectorList)) { + REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored); + return nullptr; + } + + // Ignore !important in keyframe rules + uint32_t parseFlags = eParseDeclaration_InBraces; + RefPtr declaration(ParseDeclarationBlock(parseFlags)); + if (!declaration) { + return nullptr; + } + + // Takes ownership of declaration + RefPtr rule = + new nsCSSKeyframeRule(Move(selectorList), declaration.forget(), + linenum, colnum); + return rule.forget(); +} + +bool +CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray& aSelectorList) +{ + for (;;) { + if (!GetToken(true)) { + // The first time through the loop, this means we got an empty + // list. Otherwise, it means we have a trailing comma. + return false; + } + float value; + switch (mToken.mType) { + case eCSSToken_Percentage: + value = mToken.mNumber; + break; + case eCSSToken_Ident: + if (mToken.mIdent.LowerCaseEqualsLiteral("from")) { + value = 0.0f; + break; + } + if (mToken.mIdent.LowerCaseEqualsLiteral("to")) { + value = 1.0f; + break; + } + MOZ_FALLTHROUGH; + default: + UngetToken(); + // The first time through the loop, this means we got an empty + // list. Otherwise, it means we have a trailing comma. + return false; + } + aSelectorList.AppendElement(value); + if (!ExpectSymbol(',', true)) { + return true; + } + } +} + +// supports_rule +// : "@supports" supports_condition group_rule_body +// ; +bool +CSSParserImpl::ParseSupportsRule(RuleAppendFunc aAppendFunc, void* aProcessData) +{ + bool conditionMet = false; + nsString condition; + + mScanner->StartRecording(); + + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum)) { + return false; + } + + bool parsed = ParseSupportsCondition(conditionMet); + + if (!parsed) { + mScanner->StopRecording(); + return false; + } + + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PESupportsGroupRuleStart); + mScanner->StopRecording(); + return false; + } + + UngetToken(); + mScanner->StopRecording(condition); + + // Remove the "{" that would follow the condition. + if (condition.Length() != 0) { + condition.Truncate(condition.Length() - 1); + } + + // Remove spaces from the start and end of the recorded supports condition. + condition.Trim(" ", true, true, false); + + // Record whether we are in a failing @supports, so that property parse + // errors don't get reported. + nsAutoFailingSupportsRule failing(this, conditionMet); + + RefPtr rule = new CSSSupportsRule(conditionMet, condition, + linenum, colnum); + return ParseGroupRule(rule, aAppendFunc, aProcessData); +} + +// supports_condition +// : supports_condition_in_parens supports_condition_terms +// | supports_condition_negation +// ; +bool +CSSParserImpl::ParseSupportsCondition(bool& aConditionMet) +{ + nsAutoInSupportsCondition aisc(this); + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESupportsConditionStartEOF2); + return false; + } + + UngetToken(); + + mScanner->ClearSeenBadToken(); + + if (mToken.IsSymbol('(') || + mToken.mType == eCSSToken_Function || + mToken.mType == eCSSToken_URL || + mToken.mType == eCSSToken_Bad_URL) { + bool result = ParseSupportsConditionInParens(aConditionMet) && + ParseSupportsConditionTerms(aConditionMet) && + !mScanner->SeenBadToken(); + return result; + } + + if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("not")) { + bool result = ParseSupportsConditionNegation(aConditionMet) && + !mScanner->SeenBadToken(); + return result; + } + + REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedStart); + return false; +} + +// supports_condition_negation +// : 'not' S+ supports_condition_in_parens +// ; +bool +CSSParserImpl::ParseSupportsConditionNegation(bool& aConditionMet) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESupportsConditionNotEOF); + return false; + } + + if (mToken.mType != eCSSToken_Ident || + !mToken.mIdent.LowerCaseEqualsLiteral("not")) { + REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedNot); + return false; + } + + if (!RequireWhitespace()) { + REPORT_UNEXPECTED(PESupportsWhitespaceRequired); + return false; + } + + if (ParseSupportsConditionInParens(aConditionMet)) { + aConditionMet = !aConditionMet; + return true; + } + + return false; +} + +// supports_condition_in_parens +// : '(' S* supports_condition_in_parens_inside_parens ')' S* +// | supports_condition_pref +// | general_enclosed +// ; +bool +CSSParserImpl::ParseSupportsConditionInParens(bool& aConditionMet) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESupportsConditionInParensStartEOF); + return false; + } + + if (mToken.mType == eCSSToken_URL) { + aConditionMet = false; + return true; + } + + if (AgentRulesEnabled() && + mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("-moz-bool-pref")) { + return ParseSupportsMozBoolPrefName(aConditionMet); + } + + if (mToken.mType == eCSSToken_Function || + mToken.mType == eCSSToken_Bad_URL) { + if (!SkipUntil(')')) { + REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF); + return false; + } + aConditionMet = false; + return true; + } + + if (!mToken.IsSymbol('(')) { + REPORT_UNEXPECTED_TOKEN(PESupportsConditionExpectedOpenParenOrFunction); + UngetToken(); + return false; + } + + if (!ParseSupportsConditionInParensInsideParens(aConditionMet)) { + if (!SkipUntil(')')) { + REPORT_UNEXPECTED_EOF(PESupportsConditionInParensEOF); + return false; + } + aConditionMet = false; + return true; + } + + if (!(ExpectSymbol(')', true))) { + SkipUntil(')'); + aConditionMet = false; + return true; + } + + return true; +} + +// supports_condition_pref +// : '-moz-bool-pref(' bool_pref_name ')' +// ; +bool +CSSParserImpl::ParseSupportsMozBoolPrefName(bool& aConditionMet) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_String) { + SkipUntil(')'); + return false; + } + + aConditionMet = Preferences::GetBool( + NS_ConvertUTF16toUTF8(mToken.mIdent).get()); + + if (!ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + + return true; +} + +// supports_condition_in_parens_inside_parens +// : core_declaration +// | supports_condition_negation +// | supports_condition_in_parens supports_condition_terms +// ; +bool +CSSParserImpl::ParseSupportsConditionInParensInsideParens(bool& aConditionMet) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType == eCSSToken_Ident) { + if (!mToken.mIdent.LowerCaseEqualsLiteral("not")) { + nsAutoString propertyName = mToken.mIdent; + if (!ExpectSymbol(':', true)) { + return false; + } + + nsCSSPropertyID propID = LookupEnabledProperty(propertyName); + if (propID == eCSSProperty_UNKNOWN) { + if (ExpectSymbol(')', true)) { + UngetToken(); + return false; + } + aConditionMet = false; + SkipUntil(')'); + UngetToken(); + } else if (propID == eCSSPropertyExtra_variable) { + if (ExpectSymbol(')', false)) { + UngetToken(); + return false; + } + CSSVariableDeclarations::Type variableType; + nsString variableValue; + aConditionMet = + ParseVariableDeclaration(&variableType, variableValue) && + ParsePriority() != ePriority_Error; + if (!aConditionMet) { + SkipUntil(')'); + UngetToken(); + } + } else { + if (ExpectSymbol(')', true)) { + UngetToken(); + return false; + } + aConditionMet = ParseProperty(propID) && + ParsePriority() != ePriority_Error; + if (!aConditionMet) { + SkipUntil(')'); + UngetToken(); + } + mTempData.ClearProperty(propID); + mTempData.AssertInitialState(); + } + return true; + } + + UngetToken(); + return ParseSupportsConditionNegation(aConditionMet); + } + + UngetToken(); + return ParseSupportsConditionInParens(aConditionMet) && + ParseSupportsConditionTerms(aConditionMet); +} + +// supports_condition_terms +// : S+ 'and' supports_condition_terms_after_operator('and') +// | S+ 'or' supports_condition_terms_after_operator('or') +// | +// ; +bool +CSSParserImpl::ParseSupportsConditionTerms(bool& aConditionMet) +{ + if (!RequireWhitespace() || !GetToken(false)) { + return true; + } + + if (mToken.mType != eCSSToken_Ident) { + UngetToken(); + return true; + } + + if (mToken.mIdent.LowerCaseEqualsLiteral("and")) { + return ParseSupportsConditionTermsAfterOperator(aConditionMet, eAnd); + } + + if (mToken.mIdent.LowerCaseEqualsLiteral("or")) { + return ParseSupportsConditionTermsAfterOperator(aConditionMet, eOr); + } + + UngetToken(); + return true; +} + +// supports_condition_terms_after_operator(operator) +// : S+ supports_condition_in_parens ( supports_condition_in_parens )* +// ; +bool +CSSParserImpl::ParseSupportsConditionTermsAfterOperator( + bool& aConditionMet, + CSSParserImpl::SupportsConditionTermOperator aOperator) +{ + if (!RequireWhitespace()) { + REPORT_UNEXPECTED(PESupportsWhitespaceRequired); + return false; + } + + const char* token = aOperator == eAnd ? "and" : "or"; + for (;;) { + bool termConditionMet = false; + if (!ParseSupportsConditionInParens(termConditionMet)) { + return false; + } + aConditionMet = aOperator == eAnd ? aConditionMet && termConditionMet : + aConditionMet || termConditionMet; + + if (!GetToken(true)) { + return true; + } + + if (mToken.mType != eCSSToken_Ident || + !mToken.mIdent.LowerCaseEqualsASCII(token)) { + UngetToken(); + return true; + } + } +} + +bool +CSSParserImpl::ParseCounterStyleRule(RuleAppendFunc aAppendFunc, void* aData) +{ + nsAutoString name; + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !ParseCounterStyleName(name, true)) { + REPORT_UNEXPECTED_TOKEN(PECounterStyleNotIdent); + return false; + } + + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PECounterStyleBadBlockStart); + return false; + } + + RefPtr rule = new nsCSSCounterStyleRule(name, + linenum, + colnum); + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PECounterStyleEOF); + break; + } + if (mToken.IsSymbol('}')) { + break; + } + if (mToken.IsSymbol(';')) { + continue; + } + + if (!ParseCounterDescriptor(rule)) { + REPORT_UNEXPECTED(PEDeclSkipped); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + REPORT_UNEXPECTED_EOF(PECounterStyleEOF); + break; + } + } + } + + int32_t system = rule->GetSystem(); + bool isCorrect = false; + switch (system) { + case NS_STYLE_COUNTER_SYSTEM_CYCLIC: + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC: + case NS_STYLE_COUNTER_SYSTEM_FIXED: { + // check whether symbols is set and the length is sufficient + const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols); + if (symbols.GetUnit() == eCSSUnit_List && + nsCSSCounterStyleRule::CheckDescValue( + system, eCSSCounterDesc_Symbols, symbols)) { + isCorrect = true; + } + break; + } + case NS_STYLE_COUNTER_SYSTEM_ADDITIVE: { + // for additive system, additive-symbols must be set + const nsCSSValue& symbols = + rule->GetDesc(eCSSCounterDesc_AdditiveSymbols); + if (symbols.GetUnit() == eCSSUnit_PairList) { + isCorrect = true; + } + break; + } + case NS_STYLE_COUNTER_SYSTEM_EXTENDS: { + // for extends system, symbols & additive-symbols must not be set + const nsCSSValue& symbols = rule->GetDesc(eCSSCounterDesc_Symbols); + const nsCSSValue& additiveSymbols = + rule->GetDesc(eCSSCounterDesc_AdditiveSymbols); + if (symbols.GetUnit() == eCSSUnit_Null && + additiveSymbols.GetUnit() == eCSSUnit_Null) { + isCorrect = true; + } + break; + } + default: + NS_NOTREACHED("unknown system"); + } + + if (isCorrect) { + (*aAppendFunc)(rule, aData); + } + return true; +} + +bool +CSSParserImpl::ParseCounterStyleName(nsAString& aName, bool aForDefinition) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_Ident) { + UngetToken(); + return false; + } + + static const nsCSSKeyword kReservedNames[] = { + eCSSKeyword_none, + eCSSKeyword_decimal, + eCSSKeyword_UNKNOWN + }; + + nsCSSValue value; // we don't actually care about the value + if (!ParseCustomIdent(value, mToken.mIdent, + aForDefinition ? kReservedNames : nullptr)) { + REPORT_UNEXPECTED_TOKEN(PECounterStyleBadName); + UngetToken(); + return false; + } + + aName = mToken.mIdent; + if (nsCSSProps::IsPredefinedCounterStyle(aName)) { + ToLowerCase(aName); + } + return true; +} + +bool +CSSParserImpl::ParseCounterStyleNameValue(nsCSSValue& aValue) +{ + nsString name; + if (ParseCounterStyleName(name, false)) { + aValue.SetStringValue(name, eCSSUnit_Ident); + return true; + } + return false; +} + +bool +CSSParserImpl::ParseCounterDescriptor(nsCSSCounterStyleRule* aRule) +{ + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PECounterDescExpected); + return false; + } + + nsString descName = mToken.mIdent; + if (!ExpectSymbol(':', true)) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); + OUTPUT_ERROR(); + return false; + } + + nsCSSCounterDesc descID = nsCSSProps::LookupCounterDesc(descName); + nsCSSValue value; + + if (descID == eCSSCounterDesc_UNKNOWN) { + REPORT_UNEXPECTED_P(PEUnknownCounterDesc, descName); + return false; + } + + if (!ParseCounterDescriptorValue(descID, value)) { + REPORT_UNEXPECTED_P(PEValueParsingError, descName); + return false; + } + + if (!ExpectEndProperty()) { + return false; + } + + aRule->SetDesc(descID, value); + return true; +} + +bool +CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, + nsCSSValue& aValue) +{ + // Should also include VARIANT_IMAGE, but it is not supported currently. + // See bug 1024179. + static const int32_t VARIANT_COUNTER_SYMBOL = + VARIANT_STRING | VARIANT_IDENTIFIER; + + switch (aDescID) { + case eCSSCounterDesc_System: { + nsCSSValue system; + if (!ParseEnum(system, nsCSSProps::kCounterSystemKTable)) { + return false; + } + switch (system.GetIntValue()) { + case NS_STYLE_COUNTER_SYSTEM_FIXED: { + nsCSSValue start; + if (!ParseSingleTokenVariant(start, VARIANT_INTEGER, nullptr)) { + start.SetIntValue(1, eCSSUnit_Integer); + } + aValue.SetPairValue(system, start); + return true; + } + case NS_STYLE_COUNTER_SYSTEM_EXTENDS: { + nsCSSValue name; + if (!ParseCounterStyleNameValue(name)) { + REPORT_UNEXPECTED_TOKEN(PECounterExtendsNotIdent); + return false; + } + aValue.SetPairValue(system, name); + return true; + } + default: + aValue = system; + return true; + } + } + + case eCSSCounterDesc_Negative: { + nsCSSValue first, second; + if (!ParseSingleTokenVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) { + return false; + } + if (!ParseSingleTokenVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) { + aValue = first; + } else { + aValue.SetPairValue(first, second); + } + return true; + } + + case eCSSCounterDesc_Prefix: + case eCSSCounterDesc_Suffix: + return ParseSingleTokenVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr); + + case eCSSCounterDesc_Range: { + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) { + return true; + } + nsCSSValuePairList* item = aValue.SetPairListValue(); + for (;;) { + nsCSSValuePair pair; + if (!ParseCounterRange(pair)) { + return false; + } + item->mXValue = pair.mXValue; + item->mYValue = pair.mYValue; + if (!ExpectSymbol(',', true)) { + return true; + } + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + // should always return in the loop + } + + case eCSSCounterDesc_Pad: { + nsCSSValue width, symbol; + bool hasWidth = ParseNonNegativeInteger(width); + if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || + (!hasWidth && !ParseNonNegativeInteger(width))) { + return false; + } + aValue.SetPairValue(width, symbol); + return true; + } + + case eCSSCounterDesc_Fallback: + return ParseCounterStyleNameValue(aValue); + + case eCSSCounterDesc_Symbols: { + nsCSSValueList* item = nullptr; + for (;;) { + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) { + return !!item; + } + if (!item) { + item = aValue.SetListValue(); + } else { + item->mNext = new nsCSSValueList; + item = item->mNext; + } + item->mValue = value; + } + // should always return in the loop + } + + case eCSSCounterDesc_AdditiveSymbols: { + nsCSSValuePairList* item = nullptr; + int32_t lastWeight = -1; + for (;;) { + nsCSSValue weight, symbol; + bool hasWeight = ParseNonNegativeInteger(weight); + if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || + (!hasWeight && !ParseNonNegativeInteger(weight))) { + return false; + } + if (lastWeight != -1 && weight.GetIntValue() >= lastWeight) { + REPORT_UNEXPECTED(PECounterASWeight); + return false; + } + lastWeight = weight.GetIntValue(); + if (!item) { + item = aValue.SetPairListValue(); + } else { + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + item->mXValue = weight; + item->mYValue = symbol; + if (!ExpectSymbol(',', true)) { + return true; + } + } + // should always return in the loop + } + + case eCSSCounterDesc_SpeakAs: + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD, + nsCSSProps::kCounterSpeakAsKTable)) { + if (aValue.GetUnit() == eCSSUnit_Enumerated && + aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) { + // Currently spell-out is not supported, so it is explicitly + // rejected here rather than parsed as a custom identifier. + // See bug 1024178. + return false; + } + return true; + } + return ParseCounterStyleNameValue(aValue); + + default: + NS_NOTREACHED("unknown descriptor"); + return false; + } +} + +bool +CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair) +{ + static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD; + nsCSSValue lower, upper; + if (!ParseSingleTokenVariant(lower, VARIANT_BOUND, + nsCSSProps::kCounterRangeKTable) || + !ParseSingleTokenVariant(upper, VARIANT_BOUND, + nsCSSProps::kCounterRangeKTable)) { + return false; + } + if (lower.GetUnit() != eCSSUnit_Enumerated && + upper.GetUnit() != eCSSUnit_Enumerated && + lower.GetIntValue() > upper.GetIntValue()) { + return false; + } + aPair = nsCSSValuePair(lower, upper); + return true; +} + +bool +CSSParserImpl::SkipUntil(char16_t aStopSymbol) +{ + nsCSSToken* tk = &mToken; + AutoTArray stack; + stack.AppendElement(aStopSymbol); + for (;;) { + if (!GetToken(true)) { + return false; + } + if (eCSSToken_Symbol == tk->mType) { + char16_t symbol = tk->mSymbol; + uint32_t stackTopIndex = stack.Length() - 1; + if (symbol == stack.ElementAt(stackTopIndex)) { + stack.RemoveElementAt(stackTopIndex); + if (stackTopIndex == 0) { + return true; + } + + // Just handle out-of-memory by parsing incorrectly. It's + // highly unlikely we're dealing with a legitimate style sheet + // anyway. + } else if ('{' == symbol) { + stack.AppendElement('}'); + } else if ('[' == symbol) { + stack.AppendElement(']'); + } else if ('(' == symbol) { + stack.AppendElement(')'); + } + } else if (eCSSToken_Function == tk->mType || + eCSSToken_Bad_URL == tk->mType) { + stack.AppendElement(')'); + } + } +} + +bool +CSSParserImpl::SkipBalancedContentUntil(char16_t aStopSymbol) +{ + nsCSSToken* tk = &mToken; + AutoTArray stack; + stack.AppendElement(aStopSymbol); + for (;;) { + if (!GetToken(true)) { + return true; + } + if (eCSSToken_Symbol == tk->mType) { + char16_t symbol = tk->mSymbol; + uint32_t stackTopIndex = stack.Length() - 1; + if (symbol == stack.ElementAt(stackTopIndex)) { + stack.RemoveElementAt(stackTopIndex); + if (stackTopIndex == 0) { + return true; + } + + // Just handle out-of-memory by parsing incorrectly. It's + // highly unlikely we're dealing with a legitimate style sheet + // anyway. + } else if ('{' == symbol) { + stack.AppendElement('}'); + } else if ('[' == symbol) { + stack.AppendElement(']'); + } else if ('(' == symbol) { + stack.AppendElement(')'); + } else if (')' == symbol || + ']' == symbol || + '}' == symbol) { + UngetToken(); + return false; + } + } else if (eCSSToken_Function == tk->mType || + eCSSToken_Bad_URL == tk->mType) { + stack.AppendElement(')'); + } + } +} + +void +CSSParserImpl::SkipUntilOneOf(const char16_t* aStopSymbolChars) +{ + nsCSSToken* tk = &mToken; + nsDependentString stopSymbolChars(aStopSymbolChars); + for (;;) { + if (!GetToken(true)) { + break; + } + if (eCSSToken_Symbol == tk->mType) { + char16_t symbol = tk->mSymbol; + if (stopSymbolChars.FindChar(symbol) != -1) { + break; + } else if ('{' == symbol) { + SkipUntil('}'); + } else if ('[' == symbol) { + SkipUntil(']'); + } else if ('(' == symbol) { + SkipUntil(')'); + } + } else if (eCSSToken_Function == tk->mType || + eCSSToken_Bad_URL == tk->mType) { + SkipUntil(')'); + } + } +} + +void +CSSParserImpl::SkipUntilAllOf(const StopSymbolCharStack& aStopSymbolChars) +{ + uint32_t i = aStopSymbolChars.Length(); + while (i--) { + SkipUntil(aStopSymbolChars[i]); + } +} + +bool +CSSParserImpl::SkipDeclaration(bool aCheckForBraces) +{ + nsCSSToken* tk = &mToken; + for (;;) { + if (!GetToken(true)) { + if (aCheckForBraces) { + REPORT_UNEXPECTED_EOF(PESkipDeclBraceEOF); + } + return false; + } + if (eCSSToken_Symbol == tk->mType) { + char16_t symbol = tk->mSymbol; + if (';' == symbol) { + break; + } + if (aCheckForBraces) { + if ('}' == symbol) { + UngetToken(); + break; + } + } + if ('{' == symbol) { + SkipUntil('}'); + } else if ('(' == symbol) { + SkipUntil(')'); + } else if ('[' == symbol) { + SkipUntil(']'); + } + } else if (eCSSToken_Function == tk->mType || + eCSSToken_Bad_URL == tk->mType) { + SkipUntil(')'); + } + } + return true; +} + +void +CSSParserImpl::SkipRuleSet(bool aInsideBraces) +{ + nsCSSToken* tk = &mToken; + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESkipRSBraceEOF); + break; + } + if (eCSSToken_Symbol == tk->mType) { + char16_t symbol = tk->mSymbol; + if ('}' == symbol && aInsideBraces) { + // leave block closer for higher-level grammar to consume + UngetToken(); + break; + } else if ('{' == symbol) { + SkipUntil('}'); + break; + } else if ('(' == symbol) { + SkipUntil(')'); + } else if ('[' == symbol) { + SkipUntil(']'); + } + } else if (eCSSToken_Function == tk->mType || + eCSSToken_Bad_URL == tk->mType) { + SkipUntil(')'); + } + } +} + +void +CSSParserImpl::PushGroup(css::GroupRule* aRule) +{ + mGroupStack.AppendElement(aRule); +} + +void +CSSParserImpl::PopGroup() +{ + uint32_t count = mGroupStack.Length(); + if (0 < count) { + mGroupStack.RemoveElementAt(count - 1); + } +} + +void +CSSParserImpl::AppendRule(css::Rule* aRule) +{ + uint32_t count = mGroupStack.Length(); + if (0 < count) { + mGroupStack[count - 1]->AppendStyleRule(aRule); + } + else { + mSheet->AppendStyleRule(aRule); + } +} + +bool +CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, + bool aInsideBraces) +{ + // First get the list of selectors for the rule + nsCSSSelectorList* slist = nullptr; + uint32_t linenum, colnum; + if (!GetNextTokenLocation(true, &linenum, &colnum) || + !ParseSelectorList(slist, char16_t('{'))) { + REPORT_UNEXPECTED(PEBadSelectorRSIgnored); + OUTPUT_ERROR(); + SkipRuleSet(aInsideBraces); + return false; + } + NS_ASSERTION(nullptr != slist, "null selector list"); + CLEAR_ERROR(); + + // Next parse the declaration block + uint32_t parseFlags = eParseDeclaration_InBraces | + eParseDeclaration_AllowImportant; + RefPtr declaration = ParseDeclarationBlock(parseFlags); + if (nullptr == declaration) { + delete slist; + return false; + } + +#if 0 + slist->Dump(); + fputs("{\n", stdout); + declaration->List(); + fputs("}\n", stdout); +#endif + + // Translate the selector list and declaration block into style data + + RefPtr rule = new css::StyleRule(slist, declaration, + linenum, colnum); + (*aAppendFunc)(rule, aData); + + return true; +} + +bool +CSSParserImpl::ParseSelectorList(nsCSSSelectorList*& aListHead, + char16_t aStopChar) +{ + nsCSSSelectorList* list = nullptr; + if (! ParseSelectorGroup(list)) { + // must have at least one selector group + aListHead = nullptr; + return false; + } + NS_ASSERTION(nullptr != list, "no selector list"); + aListHead = list; + + // After that there must either be a "," or a "{" (the latter if + // StopChar is nonzero) + nsCSSToken* tk = &mToken; + for (;;) { + if (! GetToken(true)) { + if (aStopChar == char16_t(0)) { + return true; + } + + REPORT_UNEXPECTED_EOF(PESelectorListExtraEOF); + break; + } + + if (eCSSToken_Symbol == tk->mType) { + if (',' == tk->mSymbol) { + nsCSSSelectorList* newList = nullptr; + // Another selector group must follow + if (! ParseSelectorGroup(newList)) { + break; + } + // add new list to the end of the selector list + list->mNext = newList; + list = newList; + continue; + } else if (aStopChar == tk->mSymbol && aStopChar != char16_t(0)) { + UngetToken(); + return true; + } + } + REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); + UngetToken(); + break; + } + + delete aListHead; + aListHead = nullptr; + return false; +} + +static bool IsUniversalSelector(const nsCSSSelector& aSelector) +{ + return bool((aSelector.mNameSpace == kNameSpaceID_Unknown) && + (aSelector.mLowercaseTag == nullptr) && + (aSelector.mIDList == nullptr) && + (aSelector.mClassList == nullptr) && + (aSelector.mAttrList == nullptr) && + (aSelector.mNegations == nullptr) && + (aSelector.mPseudoClassList == nullptr)); +} + +bool +CSSParserImpl::ParseSelectorGroup(nsCSSSelectorList*& aList) +{ + char16_t combinator = 0; + nsAutoPtr list(new nsCSSSelectorList()); + + for (;;) { + if (!ParseSelector(list, combinator)) { + return false; + } + + // Look for a combinator. + if (!GetToken(false)) { + break; // EOF ok here + } + + combinator = char16_t(0); + if (mToken.mType == eCSSToken_Whitespace) { + if (!GetToken(true)) { + break; // EOF ok here + } + combinator = char16_t(' '); + } + + if (mToken.mType != eCSSToken_Symbol) { + UngetToken(); // not a combinator + } else { + char16_t symbol = mToken.mSymbol; + if (symbol == '+' || symbol == '>' || symbol == '~') { + combinator = mToken.mSymbol; + } else { + UngetToken(); // not a combinator + if (symbol == ',' || symbol == '{' || symbol == ')') { + break; // end of selector group + } + } + } + + if (!combinator) { + REPORT_UNEXPECTED_TOKEN(PESelectorListExtra); + return false; + } + } + + aList = list.forget(); + return true; +} + +#define SEL_MASK_NSPACE 0x01 +#define SEL_MASK_ELEM 0x02 +#define SEL_MASK_ID 0x04 +#define SEL_MASK_CLASS 0x08 +#define SEL_MASK_ATTRIB 0x10 +#define SEL_MASK_PCLASS 0x20 +#define SEL_MASK_PELEM 0x40 + +// +// Parses an ID selector #name +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParseIDSelector(int32_t& aDataMask, + nsCSSSelector& aSelector) +{ + NS_ASSERTION(!mToken.mIdent.IsEmpty(), + "Empty mIdent in eCSSToken_ID token?"); + aDataMask |= SEL_MASK_ID; + aSelector.AddID(mToken.mIdent); + return eSelectorParsingStatus_Continue; +} + +// +// Parses a class selector .name +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParseClassSelector(int32_t& aDataMask, + nsCSSSelector& aSelector) +{ + if (! GetToken(false)) { // get ident + REPORT_UNEXPECTED_EOF(PEClassSelEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident != mToken.mType) { // malformed selector + REPORT_UNEXPECTED_TOKEN(PEClassSelNotIdent); + UngetToken(); + return eSelectorParsingStatus_Error; + } + aDataMask |= SEL_MASK_CLASS; + + aSelector.AddClass(mToken.mIdent); + + return eSelectorParsingStatus_Continue; +} + +// +// Parse a type element selector or a universal selector +// namespace|type or namespace|* or *|* or * +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + bool aIsNegated) +{ + nsAutoString buffer; + if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace + if (ExpectSymbol('|', false)) { // was namespace + aDataMask |= SEL_MASK_NSPACE; + aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard + + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PETypeSelEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // element name + aDataMask |= SEL_MASK_ELEM; + + aSelector.SetTag(mToken.mIdent); + } + else if (mToken.IsSymbol('*')) { // universal selector + aDataMask |= SEL_MASK_ELEM; + // don't set tag + } + else { + REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + else { // was universal element selector + SetDefaultNamespaceOnSelector(aSelector); + aDataMask |= SEL_MASK_ELEM; + // don't set any tag in the selector + } + if (! GetToken(false)) { // premature eof is ok (here!) + return eSelectorParsingStatus_Done; + } + } + else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name + buffer = mToken.mIdent; // hang on to ident + + if (ExpectSymbol('|', false)) { // was namespace + aDataMask |= SEL_MASK_NSPACE; + int32_t nameSpaceID = GetNamespaceIdForPrefix(buffer); + if (nameSpaceID == kNameSpaceID_Unknown) { + return eSelectorParsingStatus_Error; + } + aSelector.SetNameSpace(nameSpaceID); + + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PETypeSelEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // element name + aDataMask |= SEL_MASK_ELEM; + aSelector.SetTag(mToken.mIdent); + } + else if (mToken.IsSymbol('*')) { // universal selector + aDataMask |= SEL_MASK_ELEM; + // don't set tag + } + else { + REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + else { // was element name + SetDefaultNamespaceOnSelector(aSelector); + aSelector.SetTag(buffer); + + aDataMask |= SEL_MASK_ELEM; + } + if (! GetToken(false)) { // premature eof is ok (here!) + return eSelectorParsingStatus_Done; + } + } + else if (mToken.IsSymbol('|')) { // No namespace + aDataMask |= SEL_MASK_NSPACE; + aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace + + // get mandatory tag + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PETypeSelEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // element name + aDataMask |= SEL_MASK_ELEM; + aSelector.SetTag(mToken.mIdent); + } + else if (mToken.IsSymbol('*')) { // universal selector + aDataMask |= SEL_MASK_ELEM; + // don't set tag + } + else { + REPORT_UNEXPECTED_TOKEN(PETypeSelNotType); + UngetToken(); + return eSelectorParsingStatus_Error; + } + if (! GetToken(false)) { // premature eof is ok (here!) + return eSelectorParsingStatus_Done; + } + } + else { + SetDefaultNamespaceOnSelector(aSelector); + } + + if (aIsNegated) { + // restore last token read in case of a negated type selector + UngetToken(); + } + return eSelectorParsingStatus_Continue; +} + +// +// Parse attribute selectors [attr], [attr=value], [attr|=value], +// [attr~=value], [attr^=value], [attr$=value] and [attr*=value] +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParseAttributeSelector(int32_t& aDataMask, + nsCSSSelector& aSelector) +{ + if (! GetToken(true)) { // premature EOF + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return eSelectorParsingStatus_Error; + } + + int32_t nameSpaceID = kNameSpaceID_None; + nsAutoString attr; + if (mToken.IsSymbol('*')) { // wildcard namespace + nameSpaceID = kNameSpaceID_Unknown; + if (ExpectSymbol('|', false)) { + if (! GetToken(false)) { // premature EOF + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttSelNoBar); + return eSelectorParsingStatus_Error; + } + } + else if (mToken.IsSymbol('|')) { // NO namespace + if (! GetToken(false)) { // premature EOF + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace + attr = mToken.mIdent; // hang on to it + if (ExpectSymbol('|', false)) { // was a namespace + nameSpaceID = GetNamespaceIdForPrefix(attr); + if (nameSpaceID == kNameSpaceID_Unknown) { + return eSelectorParsingStatus_Error; + } + if (! GetToken(false)) { // premature EOF + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return eSelectorParsingStatus_Error; + } + if (eCSSToken_Ident == mToken.mType) { // attr name + attr = mToken.mIdent; + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + } + else { // malformed + REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + bool gotEOF = false; + if (! GetToken(true)) { // premature EOF + // Treat this just like we saw a ']', but do still output the + // warning, similar to what ExpectSymbol does. + REPORT_UNEXPECTED_EOF(PEAttSelInnerEOF); + gotEOF = true; + } + if (gotEOF || + (eCSSToken_Symbol == mToken.mType) || + (eCSSToken_Includes == mToken.mType) || + (eCSSToken_Dashmatch == mToken.mType) || + (eCSSToken_Beginsmatch == mToken.mType) || + (eCSSToken_Endsmatch == mToken.mType) || + (eCSSToken_Containsmatch == mToken.mType)) { + uint8_t func; + // Important: Check the EOF/']' case first, since if gotEOF we + // don't want to be examining mToken. + if (gotEOF || ']' == mToken.mSymbol) { + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr); + func = NS_ATTR_FUNC_SET; + } + else if (eCSSToken_Includes == mToken.mType) { + func = NS_ATTR_FUNC_INCLUDES; + } + else if (eCSSToken_Dashmatch == mToken.mType) { + func = NS_ATTR_FUNC_DASHMATCH; + } + else if (eCSSToken_Beginsmatch == mToken.mType) { + func = NS_ATTR_FUNC_BEGINSMATCH; + } + else if (eCSSToken_Endsmatch == mToken.mType) { + func = NS_ATTR_FUNC_ENDSMATCH; + } + else if (eCSSToken_Containsmatch == mToken.mType) { + func = NS_ATTR_FUNC_CONTAINSMATCH; + } + else if ('=' == mToken.mSymbol) { + func = NS_ATTR_FUNC_EQUALS; + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected); + UngetToken(); // bad function + return eSelectorParsingStatus_Error; + } + if (NS_ATTR_FUNC_SET != func) { // get value + if (! GetToken(true)) { // premature EOF + REPORT_UNEXPECTED_EOF(PEAttSelValueEOF); + return eSelectorParsingStatus_Error; + } + if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) { + nsAutoString value(mToken.mIdent); + // Avoid duplicating the eof handling by just not checking for + // the 'i' annotation if we got eof. + typedef nsAttrSelector::ValueCaseSensitivity ValueCaseSensitivity; + ValueCaseSensitivity valueCaseSensitivity = + ValueCaseSensitivity::CaseSensitive; + bool eof = !GetToken(true); + if (!eof) { + if (eCSSToken_Ident == mToken.mType && + mToken.mIdent.LowerCaseEqualsLiteral("i")) { + valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitive; + eof = !GetToken(true); + } + } + bool gotClosingBracket; + if (eof) { // premature EOF + // Report a warning, but then treat it as a closing bracket. + REPORT_UNEXPECTED_EOF(PEAttSelCloseEOF); + gotClosingBracket = true; + } else { + gotClosingBracket = mToken.IsSymbol(']'); + } + if (gotClosingBracket) { + // For cases when this style sheet is applied to an HTML + // element in an HTML document, and the attribute selector is + // for a non-namespaced attribute, then check to see if it's + // one of the known attributes whose VALUE is + // case-insensitive. + if (valueCaseSensitivity != ValueCaseSensitivity::CaseInsensitive && + nameSpaceID == kNameSpaceID_None) { + static const char* caseInsensitiveHTMLAttribute[] = { + // list based on http://www.w3.org/TR/html4/ + "lang", + "dir", + "http-equiv", + "text", + "link", + "vlink", + "alink", + "compact", + "align", + "frame", + "rules", + "valign", + "scope", + "axis", + "nowrap", + "hreflang", + "rel", + "rev", + "charset", + "codetype", + "declare", + "valuetype", + "shape", + "nohref", + "media", + "bgcolor", + "clear", + "color", + "face", + "noshade", + "noresize", + "scrolling", + "target", + "method", + "enctype", + "accept-charset", + "accept", + "checked", + "multiple", + "selected", + "disabled", + "readonly", + "language", + "defer", + "type", + // additional attributes not in HTML4 + "direction", // marquee + nullptr + }; + short i = 0; + const char* htmlAttr; + while ((htmlAttr = caseInsensitiveHTMLAttribute[i++])) { + if (attr.LowerCaseEqualsASCII(htmlAttr)) { + valueCaseSensitivity = ValueCaseSensitivity::CaseInsensitiveInHTML; + break; + } + } + } + aDataMask |= SEL_MASK_ATTRIB; + aSelector.AddAttribute(nameSpaceID, attr, func, value, + valueCaseSensitivity); + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttSelNoClose); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttSelBadValue); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttSelUnexpected); + UngetToken(); // bad dog, no biscut! + return eSelectorParsingStatus_Error; + } + return eSelectorParsingStatus_Continue; +} + +// +// Parse pseudo-classes and pseudo-elements +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, + nsCSSSelector& aSelector, + bool aIsNegated, + nsIAtom** aPseudoElement, + nsAtomList** aPseudoElementArgs, + CSSPseudoElementType* aPseudoElementType) +{ + NS_ASSERTION(aIsNegated || (aPseudoElement && aPseudoElementArgs), + "expected location to store pseudo element"); + NS_ASSERTION(!aIsNegated || (!aPseudoElement && !aPseudoElementArgs), + "negated selectors shouldn't have a place to store " + "pseudo elements"); + if (! GetToken(false)) { // premature eof + REPORT_UNEXPECTED_EOF(PEPseudoSelEOF); + return eSelectorParsingStatus_Error; + } + + // First, find out whether we are parsing a CSS3 pseudo-element + bool parsingPseudoElement = false; + if (mToken.IsSymbol(':')) { + parsingPseudoElement = true; + if (! GetToken(false)) { // premature eof + REPORT_UNEXPECTED_EOF(PEPseudoSelEOF); + return eSelectorParsingStatus_Error; + } + } + + // Do some sanity-checking on the token + if (eCSSToken_Ident != mToken.mType && eCSSToken_Function != mToken.mType) { + // malformed selector + REPORT_UNEXPECTED_TOKEN(PEPseudoSelBadName); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + // OK, now we know we have an mIdent. Atomize it. All the atoms, for + // pseudo-classes as well as pseudo-elements, start with a single ':'. + nsAutoString buffer; + buffer.Append(char16_t(':')); + buffer.Append(mToken.mIdent); + nsContentUtils::ASCIIToLower(buffer); + nsCOMPtr pseudo = NS_Atomize(buffer); + + // stash away some info about this pseudo so we only have to get it once. + bool isTreePseudo = false; + CSSEnabledState enabledState = EnabledState(); + CSSPseudoElementType pseudoElementType = + nsCSSPseudoElements::GetPseudoType(pseudo, enabledState); + CSSPseudoClassType pseudoClassType = + nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState); + bool pseudoClassIsUserAction = + nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType); + + if (nsCSSAnonBoxes::IsNonElement(pseudo)) { + // Non-element anonymous boxes should not match any rule. + REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + // We currently allow :-moz-placeholder and ::-moz-placeholder and + // ::placeholder. We have to be a bit stricter regarding the + // pseudo-element parsing rules. + if (pseudoElementType == CSSPseudoElementType::placeholder && + pseudoClassType == CSSPseudoClassType::mozPlaceholder) { + if (parsingPseudoElement) { + pseudoClassType = CSSPseudoClassType::NotPseudo; + } else { + pseudoElementType = CSSPseudoElementType::NotPseudo; + } + } + +#ifdef MOZ_XUL + isTreePseudo = (pseudoElementType == CSSPseudoElementType::XULTree); + // If a tree pseudo-element is using the function syntax, it will + // get isTree set here and will pass the check below that only + // allows functions if they are in our list of things allowed to be + // functions. If it is _not_ using the function syntax, isTree will + // be false, and it will still pass that check. So the tree + // pseudo-elements are allowed to be either functions or not, as + // desired. + bool isTree = (eCSSToken_Function == mToken.mType) && isTreePseudo; +#endif + bool isPseudoElement = (pseudoElementType < CSSPseudoElementType::Count); + // anonymous boxes are only allowed if they're the tree boxes or we have + // enabled agent rules + bool isAnonBox = isTreePseudo || + (pseudoElementType == CSSPseudoElementType::AnonBox && + AgentRulesEnabled()); + bool isPseudoClass = + (pseudoClassType != CSSPseudoClassType::NotPseudo); + + NS_ASSERTION(!isPseudoClass || + pseudoElementType == CSSPseudoElementType::NotPseudo, + "Why is this atom both a pseudo-class and a pseudo-element?"); + NS_ASSERTION(isPseudoClass + isPseudoElement + isAnonBox <= 1, + "Shouldn't be more than one of these"); + + if (!isPseudoClass && !isPseudoElement && !isAnonBox) { + // Not a pseudo-class, not a pseudo-element.... forget it + REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + // If it's a function token, it better be on our "ok" list, and if the name + // is that of a function pseudo it better be a function token + if ((eCSSToken_Function == mToken.mType) != + ( +#ifdef MOZ_XUL + isTree || +#endif + CSSPseudoClassType::negation == pseudoClassType || + nsCSSPseudoClasses::HasStringArg(pseudoClassType) || + nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) || + nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) { + // There are no other function pseudos + REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + // If it starts with "::", it better be a pseudo-element + if (parsingPseudoElement && + !isPseudoElement && + !isAnonBox) { + REPORT_UNEXPECTED_TOKEN(PEPseudoSelNotPE); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + if (aSelector.IsPseudoElement()) { + CSSPseudoElementType type = aSelector.PseudoType(); + if (type >= CSSPseudoElementType::Count || + !nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) { + // We only allow user action pseudo-classes on certain pseudo-elements. + REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC); + UngetToken(); + return eSelectorParsingStatus_Error; + } + if (!isPseudoClass || !pseudoClassIsUserAction) { + // CSS 4 Selectors says that pseudo-elements can only be followed by + // a user action pseudo-class. + REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } + + if (!parsingPseudoElement && + CSSPseudoClassType::negation == pseudoClassType) { + if (aIsNegated) { // :not() can't be itself negated + REPORT_UNEXPECTED_TOKEN(PEPseudoSelDoubleNot); + UngetToken(); + return eSelectorParsingStatus_Error; + } + // CSS 3 Negation pseudo-class takes one simple selector as argument + nsSelectorParsingStatus parsingStatus = + ParseNegatedSimpleSelector(aDataMask, aSelector); + if (eSelectorParsingStatus_Continue != parsingStatus) { + return parsingStatus; + } + } + else if (!parsingPseudoElement && isPseudoClass) { + aDataMask |= SEL_MASK_PCLASS; + if (eCSSToken_Function == mToken.mType) { + nsSelectorParsingStatus parsingStatus; + if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) { + parsingStatus = + ParsePseudoClassWithIdentArg(aSelector, pseudoClassType); + } + else if (nsCSSPseudoClasses::HasNthPairArg(pseudoClassType)) { + parsingStatus = + ParsePseudoClassWithNthPairArg(aSelector, pseudoClassType); + } + else { + MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType), + "unexpected pseudo with function token"); + parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector, + pseudoClassType); + } + if (eSelectorParsingStatus_Continue != parsingStatus) { + if (eSelectorParsingStatus_Error == parsingStatus) { + SkipUntil(')'); + } + return parsingStatus; + } + } + else { + aSelector.AddPseudoClass(pseudoClassType); + } + } + else if (isPseudoElement || isAnonBox) { + // Pseudo-element. Make some more sanity checks. + + if (aIsNegated) { // pseudo-elements can't be negated + REPORT_UNEXPECTED_TOKEN(PEPseudoSelPEInNot); + UngetToken(); + return eSelectorParsingStatus_Error; + } + // CSS2 pseudo-elements and -moz-tree-* pseudo-elements are allowed + // to have a single ':' on them. Others (CSS3+ pseudo-elements and + // various -moz-* pseudo-elements) must have |parsingPseudoElement| + // set. + if (!parsingPseudoElement && + !nsCSSPseudoElements::IsCSS2PseudoElement(pseudo) +#ifdef MOZ_XUL + && !isTreePseudo +#endif + ) { + REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly); + UngetToken(); + return eSelectorParsingStatus_Error; + } + + if (0 == (aDataMask & SEL_MASK_PELEM)) { + aDataMask |= SEL_MASK_PELEM; + NS_ADDREF(*aPseudoElement = pseudo); + *aPseudoElementType = pseudoElementType; + +#ifdef MOZ_XUL + if (isTree) { + // We have encountered a pseudoelement of the form + // -moz-tree-xxxx(a,b,c). We parse (a,b,c) and add each + // item in the list to the pseudoclass list. They will be pulled + // from the list later along with the pseudo-element. + if (!ParseTreePseudoElement(aPseudoElementArgs)) { + return eSelectorParsingStatus_Error; + } + } +#endif + + // Pseudo-elements can only be followed by user action pseudo-classes + // or be the end of the selector. So the next non-whitespace token must + // be ':', '{' or ',' or EOF. + if (!GetToken(true)) { // premature eof is ok (here!) + return eSelectorParsingStatus_Done; + } + if (parsingPseudoElement && mToken.IsSymbol(':')) { + UngetToken(); + return eSelectorParsingStatus_Continue; + } + if ((mToken.IsSymbol('{') || mToken.IsSymbol(','))) { + UngetToken(); + return eSelectorParsingStatus_Done; + } + REPORT_UNEXPECTED_TOKEN(PEPseudoSelEndOrUserActionPC); + UngetToken(); + return eSelectorParsingStatus_Error; + } + else { // multiple pseudo elements, not legal + REPORT_UNEXPECTED_TOKEN(PEPseudoSelMultiplePE); + UngetToken(); + return eSelectorParsingStatus_Error; + } + } +#ifdef DEBUG + else { + // We should never end up here. Indeed, if we ended up here, we know (from + // the current if/else cascade) that !isPseudoElement and !isAnonBox. But + // then due to our earlier check we know that isPseudoClass. Since we + // didn't fall into the isPseudoClass case in this cascade, we must have + // parsingPseudoElement. But we've already checked the + // parsingPseudoElement && !isPseudoClass && !isAnonBox case and bailed if + // it's happened. + NS_NOTREACHED("How did this happen?"); + } +#endif + return eSelectorParsingStatus_Continue; +} + +// +// Parse the argument of a negation pseudo-class :not() +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParseNegatedSimpleSelector(int32_t& aDataMask, + nsCSSSelector& aSelector) +{ + if (! GetToken(true)) { // premature eof + REPORT_UNEXPECTED_EOF(PENegationEOF); + return eSelectorParsingStatus_Error; + } + + if (mToken.IsSymbol(')')) { + REPORT_UNEXPECTED_TOKEN(PENegationBadArg); + return eSelectorParsingStatus_Error; + } + + // Create a new nsCSSSelector and add it to the end of + // aSelector.mNegations. + // Given the current parsing rules, every selector in mNegations + // contains only one simple selector (css3 definition) within it. + // This could easily change in future versions of CSS, and the only + // thing we need to change to support that is this parsing code and the + // serialization code for nsCSSSelector. + nsCSSSelector *newSel = new nsCSSSelector(); + nsCSSSelector* negations = &aSelector; + while (negations->mNegations) { + negations = negations->mNegations; + } + negations->mNegations = newSel; + + nsSelectorParsingStatus parsingStatus; + if (eCSSToken_ID == mToken.mType) { // #id + parsingStatus = ParseIDSelector(aDataMask, *newSel); + } + else if (mToken.IsSymbol('.')) { // .class + parsingStatus = ParseClassSelector(aDataMask, *newSel); + } + else if (mToken.IsSymbol(':')) { // :pseudo + parsingStatus = ParsePseudoSelector(aDataMask, *newSel, true, + nullptr, nullptr, nullptr); + } + else if (mToken.IsSymbol('[')) { // [attribute + parsingStatus = ParseAttributeSelector(aDataMask, *newSel); + if (eSelectorParsingStatus_Error == parsingStatus) { + // Skip forward to the matching ']' + SkipUntil(']'); + } + } + else { + // then it should be a type element or universal selector + parsingStatus = ParseTypeOrUniversalSelector(aDataMask, *newSel, true); + } + if (eSelectorParsingStatus_Error == parsingStatus) { + REPORT_UNEXPECTED_TOKEN(PENegationBadInner); + SkipUntil(')'); + return parsingStatus; + } + // close the parenthesis + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PENegationNoClose); + SkipUntil(')'); + return eSelectorParsingStatus_Error; + } + + NS_ASSERTION(newSel->mNameSpace == kNameSpaceID_Unknown || + (!newSel->mIDList && !newSel->mClassList && + !newSel->mPseudoClassList && !newSel->mAttrList), + "Need to fix the serialization code to deal with this"); + + return eSelectorParsingStatus_Continue; +} + +// +// Parse the argument of a pseudo-class that has an ident arg +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType) +{ + if (! GetToken(true)) { // premature eof + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + // We expect an identifier with a language abbreviation + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotIdent); + UngetToken(); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + + // -moz-locale-dir and dir take an identifier argument. While + // only 'ltr' and 'rtl' (case-insensitively) will match anything, any + // other identifier is still valid. + if (aType == CSSPseudoClassType::mozLocaleDir || + aType == CSSPseudoClassType::mozDir || + aType == CSSPseudoClassType::dir) { + nsContentUtils::ASCIIToLower(mToken.mIdent); // case insensitive + } + + // Add the pseudo with the language parameter + aSelector.AddPseudoClass(aType, mToken.mIdent.get()); + + // close the parenthesis + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + + return eSelectorParsingStatus_Continue; +} + +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParsePseudoClassWithNthPairArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType) +{ + int32_t numbers[2] = { 0, 0 }; + int32_t sign[2] = { 1, 1 }; + bool hasSign[2] = { false, false }; + bool lookForB = true; + + // Follow the whitespace rules as proposed in + // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html + + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + + if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) { + hasSign[0] = true; + if (mToken.IsSymbol('-')) { + sign[0] = -1; + } + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + } + + // A helper function that checks if the token starts with literal string + // |aStr| using a case-insensitive match. + auto TokenBeginsWith = [this] (const nsLiteralString& aStr) { + return StringBeginsWith(mToken.mIdent, aStr, + nsASCIICaseInsensitiveStringComparator()); + }; + + if (eCSSToken_Ident == mToken.mType || eCSSToken_Dimension == mToken.mType) { + // The CSS tokenization doesn't handle :nth-child() containing - well: + // 2n-1 is a dimension + // n-1 is an identifier + // The easiest way to deal with that is to push everything from the + // minus on back onto the scanner's pushback buffer. + uint32_t truncAt = 0; + if (TokenBeginsWith(NS_LITERAL_STRING("n-"))) { + truncAt = 1; + } else if (TokenBeginsWith(NS_LITERAL_STRING("-n-")) && !hasSign[0]) { + truncAt = 2; + } + if (truncAt != 0) { + mScanner->Backup(mToken.mIdent.Length() - truncAt); + mToken.mIdent.Truncate(truncAt); + } + } + + if (eCSSToken_Ident == mToken.mType) { + if (mToken.mIdent.LowerCaseEqualsLiteral("odd") && !hasSign[0]) { + numbers[0] = 2; + numbers[1] = 1; + lookForB = false; + } + else if (mToken.mIdent.LowerCaseEqualsLiteral("even") && !hasSign[0]) { + numbers[0] = 2; + numbers[1] = 0; + lookForB = false; + } + else if (mToken.mIdent.LowerCaseEqualsLiteral("n")) { + numbers[0] = sign[0]; + } + else if (mToken.mIdent.LowerCaseEqualsLiteral("-n") && !hasSign[0]) { + numbers[0] = -1; + } + else { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + } + else if (eCSSToken_Number == mToken.mType) { + if (!mToken.mIntegerValid) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + // for +-an case + if (mToken.mHasSign && hasSign[0]) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + int32_t intValue = mToken.mInteger * sign[0]; + // for -a/**/n case + if (! GetToken(false)) { + numbers[1] = intValue; + lookForB = false; + } + else { + if (eCSSToken_Ident == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("n")) { + numbers[0] = intValue; + } + else if (eCSSToken_Ident == mToken.mType && TokenBeginsWith(NS_LITERAL_STRING("n-"))) { + numbers[0] = intValue; + mScanner->Backup(mToken.mIdent.Length() - 1); + } + else { + UngetToken(); + numbers[1] = intValue; + lookForB = false; + } + } + } + else if (eCSSToken_Dimension == mToken.mType) { + if (!mToken.mIntegerValid || !mToken.mIdent.LowerCaseEqualsLiteral("n")) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + // for +-an case + if ( mToken.mHasSign && hasSign[0] ) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + numbers[0] = mToken.mInteger * sign[0]; + } + // XXX If it's a ')', is that valid? (as 0n+0) + else { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + UngetToken(); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + if (lookForB && !mToken.IsSymbol(')')) { + // The '+' or '-' sign can optionally be separated by whitespace. + // If it is separated by whitespace from what follows it, it appears + // as a separate token rather than part of the number token. + if (mToken.IsSymbol('+') || mToken.IsSymbol('-')) { + hasSign[1] = true; + if (mToken.IsSymbol('-')) { + sign[1] = -1; + } + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + } + if (eCSSToken_Number != mToken.mType || + !mToken.mIntegerValid || mToken.mHasSign == hasSign[1]) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassArgNotNth); + UngetToken(); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + numbers[1] = mToken.mInteger * sign[1]; + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEPseudoClassArgEOF); + return eSelectorParsingStatus_Error; + } + } + if (!mToken.IsSymbol(')')) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + aSelector.AddPseudoClass(aType, numbers); + return eSelectorParsingStatus_Continue; +} + +// +// Parse the argument of a pseudo-class that has a selector list argument. +// Such selector lists cannot contain combinators, but can contain +// anything that goes between a pair of combinators. +// +CSSParserImpl::nsSelectorParsingStatus +CSSParserImpl::ParsePseudoClassWithSelectorListArg(nsCSSSelector& aSelector, + CSSPseudoClassType aType) +{ + nsAutoPtr slist; + if (! ParseSelectorList(*getter_Transfers(slist), char16_t(')'))) { + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + + // Check that none of the selectors in the list have combinators or + // pseudo-elements. + for (nsCSSSelectorList *l = slist; l; l = l->mNext) { + nsCSSSelector *s = l->mSelectors; + if (s->mNext || s->IsPseudoElement()) { + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + } + + // Add the pseudo with the selector list parameter + aSelector.AddPseudoClass(aType, slist.forget()); + + // close the parenthesis + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEPseudoClassNoClose); + return eSelectorParsingStatus_Error; // our caller calls SkipUntil(')') + } + + return eSelectorParsingStatus_Continue; +} + + +/** + * This is the format for selectors: + * operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]* + */ +bool +CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, + char16_t aPrevCombinator) +{ + if (! GetToken(true)) { + REPORT_UNEXPECTED_EOF(PESelectorEOF); + return false; + } + + nsCSSSelector* selector = aList->AddSelector(aPrevCombinator); + nsCOMPtr pseudoElement; + nsAutoPtr pseudoElementArgs; + CSSPseudoElementType pseudoElementType = CSSPseudoElementType::NotPseudo; + + int32_t dataMask = 0; + nsSelectorParsingStatus parsingStatus = + ParseTypeOrUniversalSelector(dataMask, *selector, false); + + while (parsingStatus == eSelectorParsingStatus_Continue) { + if (mToken.IsSymbol(':')) { // :pseudo + parsingStatus = ParsePseudoSelector(dataMask, *selector, false, + getter_AddRefs(pseudoElement), + getter_Transfers(pseudoElementArgs), + &pseudoElementType); + if (pseudoElement && + pseudoElementType != CSSPseudoElementType::AnonBox) { + // Pseudo-elements other than anonymous boxes are represented with + // a special ':' combinator. + + aList->mWeight += selector->CalcWeight(); + + selector = aList->AddSelector(':'); + + selector->mLowercaseTag.swap(pseudoElement); + selector->mClassList = pseudoElementArgs.forget(); + selector->SetPseudoType(pseudoElementType); + } + } else if (selector->IsPseudoElement()) { + // Once we parsed a pseudo-element, we can only parse + // pseudo-classes (and only a limited set, which + // ParsePseudoSelector knows how to handle). + parsingStatus = eSelectorParsingStatus_Done; + UngetToken(); + break; + } else if (eCSSToken_ID == mToken.mType) { // #id + parsingStatus = ParseIDSelector(dataMask, *selector); + } else if (mToken.IsSymbol('.')) { // .class + parsingStatus = ParseClassSelector(dataMask, *selector); + } + else if (mToken.IsSymbol('[')) { // [attribute + parsingStatus = ParseAttributeSelector(dataMask, *selector); + if (eSelectorParsingStatus_Error == parsingStatus) { + SkipUntil(']'); + } + } + else { // not a selector token, we're done + parsingStatus = eSelectorParsingStatus_Done; + UngetToken(); + break; + } + + if (parsingStatus != eSelectorParsingStatus_Continue) { + break; + } + + if (! GetToken(false)) { // premature eof is ok (here!) + parsingStatus = eSelectorParsingStatus_Done; + break; + } + } + + if (parsingStatus == eSelectorParsingStatus_Error) { + return false; + } + + if (!dataMask) { + if (selector->mNext) { + REPORT_UNEXPECTED(PESelectorGroupExtraCombinator); + } else { + REPORT_UNEXPECTED(PESelectorGroupNoSelector); + } + return false; + } + + if (pseudoElementType == CSSPseudoElementType::AnonBox) { + // We got an anonymous box pseudo-element; it must be the only + // thing in this selector group. + if (selector->mNext || !IsUniversalSelector(*selector)) { + REPORT_UNEXPECTED(PEAnonBoxNotAlone); + return false; + } + + // Rewrite the current selector as this pseudo-element. + // It does not contribute to selector weight. + selector->mLowercaseTag.swap(pseudoElement); + selector->mClassList = pseudoElementArgs.forget(); + selector->SetPseudoType(pseudoElementType); + return true; + } + + aList->mWeight += selector->CalcWeight(); + + return true; +} + +already_AddRefed +CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext) +{ + bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; + + MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls, + "Someone forgot to clear mWebkitBoxUnprefixState!"); + AutoRestore autoRestore(mWebkitBoxUnprefixState); + mWebkitBoxUnprefixState = eHaveNotUnprefixed; + + if (checkForBraces) { + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart); + OUTPUT_ERROR(); + return nullptr; + } + } + RefPtr declaration = new css::Declaration(); + mData.AssertInitialState(); + for (;;) { + bool changed = false; + if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) { + if (!SkipDeclaration(checkForBraces)) { + break; + } + if (checkForBraces) { + if (ExpectSymbol('}', true)) { + break; + } + } + // Since the skipped declaration didn't end the block we parse + // the next declaration. + } + } + declaration->CompressFrom(&mData); + return declaration.forget(); +} + +CSSParseResult +CSSParserImpl::ParseColor(nsCSSValue& aValue) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEColorEOF); + return CSSParseResult::NotFound; + } + + nsCSSToken* tk = &mToken; + nscolor rgba; + switch (tk->mType) { + case eCSSToken_ID: + case eCSSToken_Hash: + // #rgb, #rrggbb, #rgba, #rrggbbaa + if (NS_HexToRGBA(tk->mIdent, nsHexColorType::AllowAlpha, &rgba)) { + nsCSSUnit unit; + switch (tk->mIdent.Length()) { + case 3: + unit = eCSSUnit_ShortHexColor; + break; + case 4: + unit = eCSSUnit_ShortHexColorAlpha; + break; + case 6: + unit = eCSSUnit_HexColor; + break; + default: + MOZ_FALLTHROUGH_ASSERT("unexpected hex color length"); + case 8: + unit = eCSSUnit_HexColorAlpha; + break; + } + aValue.SetIntegerColorValue(rgba, unit); + return CSSParseResult::Ok; + } + break; + + case eCSSToken_Ident: + if (NS_ColorNameToRGB(tk->mIdent, &rgba)) { + aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); + return CSSParseResult::Ok; + } + else { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent); + if (eCSSKeyword_UNKNOWN < keyword) { // known keyword + int32_t value; + if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) { + aValue.SetIntValue(value, eCSSUnit_EnumColor); + return CSSParseResult::Ok; + } + } + } + break; + case eCSSToken_Function: { + bool isRGB; + bool isHSL; + if ((isRGB = mToken.mIdent.LowerCaseEqualsLiteral("rgb")) || + mToken.mIdent.LowerCaseEqualsLiteral("rgba")) { + // rgb() = rgb( {3} [ / ]? ) | + // rgb( {3} [ / ]? ) | + // rgb( #{3} , ? ) | + // rgb( #{3} , ? ) + // = | + // rgba is an alias of rgb. + + if (GetToken(true)) { + UngetToken(); + } + if (mToken.mType == eCSSToken_Number) { // + uint8_t r, g, b, a; + + if (ParseRGBColor(r, g, b, a)) { + aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a), + isRGB ? eCSSUnit_RGBColor : eCSSUnit_RGBAColor); + return CSSParseResult::Ok; + } + } else { // + float r, g, b, a; + + if (ParseRGBColor(r, g, b, a)) { + aValue.SetFloatColorValue(r, g, b, a, + isRGB ? eCSSUnit_PercentageRGBColor : eCSSUnit_PercentageRGBAColor); + return CSSParseResult::Ok; + } + } + SkipUntil(')'); + return CSSParseResult::Error; + } + else if ((isHSL = mToken.mIdent.LowerCaseEqualsLiteral("hsl")) || + mToken.mIdent.LowerCaseEqualsLiteral("hsla")) { + // hsl() = hsl( [ / ]? ) || + // hsl( , , , ? ) + // = | + // hsla is an alias of hsl. + + float h, s, l, a; + + if (ParseHSLColor(h, s, l, a)) { + aValue.SetFloatColorValue(h, s, l, a, + isHSL ? eCSSUnit_HSLColor : eCSSUnit_HSLAColor); + return CSSParseResult::Ok; + } + SkipUntil(')'); + return CSSParseResult::Error; + } + break; + } + default: + break; + } + + // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804) + if (mHashlessColorQuirk) { + // - If the string starts with 'a-f', the nsCSSScanner builds the + // token as a eCSSToken_Ident and we can parse the string as a + // 'xxyyzz' RGB color. + // - If it only contains '0-9' digits, the token is a + // eCSSToken_Number and it must be converted back to a 6 + // characters string to be parsed as a RGB color. + // - If it starts with '0-9' and contains any 'a-f', the token is a + // eCSSToken_Dimension, the mNumber part must be converted back to + // a string and the mIdent part must be appended to that string so + // that the resulting string has 6 characters. + // Note: This is a hack for Nav compatibility. Do not attempt to + // simplify it by hacking into the ncCSSScanner. This would be very + // bad. + nsAutoString str; + char buffer[20]; + switch (tk->mType) { + case eCSSToken_Ident: + str.Assign(tk->mIdent); + break; + + case eCSSToken_Number: + if (tk->mIntegerValid) { + SprintfLiteral(buffer, "%06d", tk->mInteger); + str.AssignWithConversion(buffer); + } + break; + + case eCSSToken_Dimension: + if (tk->mIdent.Length() <= 6) { + SprintfLiteral(buffer, "%06.0f", tk->mNumber); + nsAutoString temp; + temp.AssignWithConversion(buffer); + temp.Right(str, 6 - tk->mIdent.Length()); + str.Append(tk->mIdent); + } + break; + default: + // There is a whole bunch of cases that are + // not handled by this switch. Ignore them. + break; + } + // The hashless color quirk does not support 4 & 8 digit colors with alpha. + if (NS_HexToRGBA(str, nsHexColorType::NoAlpha, &rgba)) { + aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor); + return CSSParseResult::Ok; + } + } + + // It's not a color + REPORT_UNEXPECTED_TOKEN(PEColorNotColor); + UngetToken(); + return CSSParseResult::NotFound; +} + +bool +CSSParserImpl::ParseColorComponent(uint8_t& aComponent, Maybe aSeparator) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEColorComponentEOF); + return false; + } + + if (mToken.mType != eCSSToken_Number) { + REPORT_UNEXPECTED_TOKEN(PEExpectedNumber); + UngetToken(); + return false; + } + + float value = mToken.mNumber; + + if (aSeparator && !ExpectSymbol(*aSeparator, true)) { + REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator); + return false; + } + + if (value < 0.0f) value = 0.0f; + if (value > 255.0f) value = 255.0f; + + aComponent = NSToIntRound(value); + return true; +} + +bool +CSSParserImpl::ParseColorComponent(float& aComponent, Maybe aSeparator) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEColorComponentEOF); + return false; + } + + if (mToken.mType != eCSSToken_Percentage) { + REPORT_UNEXPECTED_TOKEN(PEExpectedPercent); + UngetToken(); + return false; + } + + float value = mToken.mNumber; + + if (aSeparator && !ExpectSymbol(*aSeparator, true)) { + REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, *aSeparator); + return false; + } + + if (value < 0.0f) value = 0.0f; + if (value > 1.0f) value = 1.0f; + + aComponent = value; + return true; +} + +bool +CSSParserImpl::ParseHue(float& aAngle) +{ + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEColorHueEOF); + return false; + } + + // + if (mToken.mType == eCSSToken_Number) { + aAngle = mToken.mNumber; + return true; + } + UngetToken(); + + // + nsCSSValue angleValue; + // The '0' value is handled by parsing, so use VARIANT_ANGLE flag + // instead of VARIANT_ANGLE_OR_ZERO. + if (ParseSingleTokenVariant(angleValue, VARIANT_ANGLE, nullptr)) { + aAngle = angleValue.GetAngleValueInDegrees(); + return true; + } + + REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrAngle); + return false; +} + +bool +CSSParserImpl::ParseHSLColor(float& aHue, float& aSaturation, float& aLightness, + float& aOpacity) +{ + // comma-less expression: + // hsl() = hsl( [ / ]? ) + // the expression with comma: + // hsl() = hsl( , , , ? ) + + const char commaSeparator = ','; + + // Parse hue. + // = | + float degreeAngle; + if (!ParseHue(degreeAngle)) { + return false; + } + aHue = degreeAngle / 360.0f; + // hue values are wraparound + aHue = aHue - floor(aHue); + // Look for a comma separator after "hue" component to determine if the + // expression is comma-less or not. + bool hasComma = ExpectSymbol(commaSeparator, true); + + // Parse saturation, lightness and opacity. + // The saturation and lightness are , so reuse the float version + // of ParseColorComponent function for them. No need to check the separator + // after 'lightness'. It will be checked in opacity value parsing. + const char separatorBeforeAlpha = hasComma ? commaSeparator : '/'; + if (ParseColorComponent(aSaturation, hasComma ? Some(commaSeparator) : Nothing()) && + ParseColorComponent(aLightness, Nothing()) && + ParseColorOpacityAndCloseParen(aOpacity, separatorBeforeAlpha)) { + return true; + } + + return false; +} + + +bool +CSSParserImpl::ParseColorOpacityAndCloseParen(uint8_t& aOpacity, + char aSeparator) +{ + float floatOpacity; + if (!ParseColorOpacityAndCloseParen(floatOpacity, aSeparator)) { + return false; + } + + uint8_t value = nsStyleUtil::FloatToColorComponent(floatOpacity); + // Need to compare to something slightly larger + // than 0.5 due to floating point inaccuracies. + NS_ASSERTION(fabs(255.0f * floatOpacity - value) <= 0.51f, + "FloatToColorComponent did something weird"); + + aOpacity = value; + return true; +} + +bool +CSSParserImpl::ParseColorOpacityAndCloseParen(float& aOpacity, + char aSeparator) +{ + if (ExpectSymbol(')', true)) { + // The optional [separator ] was omitted, so set the opacity + // to a fully-opaque value '1.0f' and return success. + aOpacity = 1.0f; + return true; + } + + if (!ExpectSymbol(aSeparator, true)) { + REPORT_UNEXPECTED_TOKEN_CHAR(PEColorComponentBadTerm, aSeparator); + return false; + } + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEColorOpacityEOF); + return false; + } + + // eCSSToken_Number or eCSSToken_Percentage. + if (mToken.mType != eCSSToken_Number && mToken.mType != eCSSToken_Percentage) { + REPORT_UNEXPECTED_TOKEN(PEExpectedNumberOrPercent); + UngetToken(); + return false; + } + + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); + return false; + } + + if (mToken.mNumber < 0.0f) { + mToken.mNumber = 0.0f; + } else if (mToken.mNumber > 1.0f) { + mToken.mNumber = 1.0f; + } + + aOpacity = mToken.mNumber; + return true; +} + +template +bool +CSSParserImpl::ParseRGBColor(ComponentType& aR, + ComponentType& aG, + ComponentType& aB, + ComponentType& aA) +{ + // comma-less expression: + // rgb() = rgb( component{3} [ / ]? ) + // the expression with comma: + // rgb() = rgb( component#{3} , ? ) + + const char commaSeparator = ','; + + // Parse R. + if (!ParseColorComponent(aR, Nothing())) { + return false; + } + // Look for a comma separator after "r" component to determine if the + // expression is comma-less or not. + bool hasComma = ExpectSymbol(commaSeparator, true); + + // Parse G, B and A. + // No need to check the separator after 'B'. It will be checked in 'A' value + // parsing. + const char separatorBeforeAlpha = hasComma ? commaSeparator : '/'; + if (ParseColorComponent(aG, hasComma ? Some(commaSeparator) : Nothing()) && + ParseColorComponent(aB, Nothing()) && + ParseColorOpacityAndCloseParen(aA, separatorBeforeAlpha)) { + return true; + } + + return false; +} + +#ifdef MOZ_XUL +bool +CSSParserImpl::ParseTreePseudoElement(nsAtomList **aPseudoElementArgs) +{ + // The argument to a tree pseudo-element is a sequence of identifiers + // that are either space- or comma-separated. (Was the intent to + // allow only comma-separated? That's not what was done.) + nsCSSSelector fakeSelector; // so we can reuse AddPseudoClass + + while (!ExpectSymbol(')', true)) { + if (!GetToken(true)) { + return false; + } + if (eCSSToken_Ident == mToken.mType) { + fakeSelector.AddClass(mToken.mIdent); + } + else if (!mToken.IsSymbol(',')) { + UngetToken(); + SkipUntil(')'); + return false; + } + } + *aPseudoElementArgs = fakeSelector.mClassList; + fakeSelector.mClassList = nullptr; + return true; +} +#endif + +nsCSSKeyword +CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr, + const KTableEntry aKeywordTable[]) +{ + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aKeywordStr); + + if (aKeywordTable == nsCSSProps::kDisplayKTable) { + // NOTE: This code will be considerably simpler once we can do away with + // all Unprefixing Service code, in bug 1259348. But for the time being, we + // have to support two different strategies for handling -webkit-box here: + // (1) "Native support" (sWebkitPrefixedAliasesEnabled): we assume that + // -webkit-box will parse correctly (via an entry in kDisplayKTable), + // and we simply make a note that we've parsed it (so that we can we + // can give later "-moz-box" styling special handling as noted below). + // (2) "Unprefixing Service support" (ShouldUseUnprefixingService): we + // convert "-webkit-box" directly to modern "flex" (& do the same for + // any later "-moz-box" styling). + // + // Note that sWebkitPrefixedAliasesEnabled and + // ShouldUseUnprefixingService() are mutually exlusive, because the latter + // explicitly defers to the former. + if ((keyword == eCSSKeyword__webkit_box || + keyword == eCSSKeyword__webkit_inline_box)) { + const bool usingUnprefixingService = ShouldUseUnprefixingService(); + if (sWebkitPrefixedAliasesEnabled || usingUnprefixingService) { + // Make a note that we're accepting some "-webkit-{inline-}box" styling, + // so we can give special treatment to subsequent "-moz-{inline}-box". + // (See special treatment below.) + if (mWebkitBoxUnprefixState == eHaveNotUnprefixed) { + mWebkitBoxUnprefixState = eHaveUnprefixed; + } + if (usingUnprefixingService) { + // When we're using the unprefixing service, we treat + // "display:-webkit-box" as if it were "display:flex" + // (and "-webkit-inline-box" as "inline-flex"). + return (keyword == eCSSKeyword__webkit_box) ? + eCSSKeyword_flex : eCSSKeyword_inline_flex; + } + } + } + + // If we've seen "display: -webkit-box" (or "-webkit-inline-box") in an + // earlier declaration and we honored it, then we have to watch out for + // later "display: -moz-box" (and "-moz-inline-box") declarations; they're + // likely just a halfhearted attempt at compatibility, and they actually + // end up stomping on our emulation of the earlier -webkit-box + // display-value, via the CSS cascade. To prevent this problem, we treat + // "display: -moz-box" & "-moz-inline-box" as if they were simply a + // repetition of the webkit equivalent that we already parsed. + if (mWebkitBoxUnprefixState == eHaveUnprefixed && + (keyword == eCSSKeyword__moz_box || + keyword == eCSSKeyword__moz_inline_box)) { + MOZ_ASSERT(sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService(), + "mDidUnprefixWebkitBoxInEarlierDecl should only be set if " + "we're supporting webkit-prefixed aliases, or if we're using " + "the css unprefixing service on this site"); + if (sWebkitPrefixedAliasesEnabled) { + return (keyword == eCSSKeyword__moz_box) ? + eCSSKeyword__webkit_box : eCSSKeyword__webkit_inline_box; + } + // (If we get here, we're using the Unprefixing Service, which means + // we're unprefixing all the way to modern flexbox display values.) + return (keyword == eCSSKeyword__moz_box) ? + eCSSKeyword_flex : eCSSKeyword_inline_flex; + } + } + + return keyword; +} + +bool +CSSParserImpl::ShouldUseUnprefixingService() const +{ + if (!sUnprefixingServiceEnabled) { + // Unprefixing is globally disabled. + return false; + } + if (sWebkitPrefixedAliasesEnabled) { + // Native webkit-prefix support is enabled, which trumps the unprefixing + // service for handling prefixed CSS. Don't try to use both at once. + return false; + } + +#ifdef NIGHTLY_BUILD + if (sUnprefixingServiceGloballyWhitelisted) { + // Unprefixing is globally whitelisted, + // so no need to check mSheetPrincipal. + return true; + } +#endif + // Unprefixing enabled; see if our principal is whitelisted for unprefixing. + return mSheetPrincipal && mSheetPrincipal->IsOnCSSUnprefixingWhitelist(); +} + +bool +CSSParserImpl::ParsePropertyWithUnprefixingService( + const nsAString& aPropertyName, + css::Declaration* aDeclaration, + uint32_t aFlags, + bool aMustCallValueAppended, + bool* aChanged, + nsCSSContextType aContext) +{ + MOZ_ASSERT(ShouldUseUnprefixingService(), + "Caller should've checked ShouldUseUnprefixingService()"); + + nsCOMPtr unprefixingSvc = + do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID); + NS_ENSURE_TRUE(unprefixingSvc, false); + + // Save the state so we can jump back to this spot if our unprefixing fails + // (so we can behave as if we didn't even try to unprefix). + nsAutoCSSParserInputStateRestorer parserStateBeforeTryingToUnprefix(this); + + // Caller has already parsed the first half of the declaration -- + // aPropertyName and the ":". Now, we record the rest of the CSS declaration + // (the part after ':') into rightHalfOfDecl. (This is the property value, + // plus anything else up to the end of the declaration -- maybe "!important", + // maybe trailing junk characters, maybe a semicolon, maybe a trailing "}".) + bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; + nsAutoString rightHalfOfDecl; + mScanner->StartRecording(); + SkipDeclaration(checkForBraces); + mScanner->StopRecording(rightHalfOfDecl); + + // Try to unprefix: + bool success; + nsAutoString unprefixedDecl; + nsresult rv = + unprefixingSvc->GenerateUnprefixedDeclaration(aPropertyName, + rightHalfOfDecl, + unprefixedDecl, &success); + if (NS_FAILED(rv) || !success) { + return false; + } + + // Attempt to parse the unprefixed declaration: + nsAutoScannerChanger scannerChanger(this, unprefixedDecl); + success = ParseDeclaration(aDeclaration, + aFlags | eParseDeclaration_FromUnprefixingSvc, + aMustCallValueAppended, aChanged, aContext); + if (success) { + // We succeeded, so we'll leave the parser pointing at the end of + // the declaration; don't restore it to the pre-recording position. + parserStateBeforeTryingToUnprefix.DoNotRestore(); + } + + return success; +} + +bool +CSSParserImpl::ParseWebkitPrefixedGradientWithService( + nsAString& aPrefixedFuncName, + nsCSSValue& aValue) +{ + MOZ_ASSERT(ShouldUseUnprefixingService(), + "Should only call if we're allowed to use unprefixing service"); + + // Record the body of the "-webkit-*gradient" function into a string. + // Note: we're already just after the opening "(". + nsAutoString prefixedFuncBody; + mScanner->StartRecording(); + bool gotCloseParen = SkipUntil(')'); + mScanner->StopRecording(prefixedFuncBody); + if (gotCloseParen) { + // Strip off trailing close-paren, so that the value we pass to the + // unprefixing service is *just* the function-body (no parens). + prefixedFuncBody.Truncate(prefixedFuncBody.Length() - 1); + } + + // NOTE: Even if we fail, we'll be leaving the parser's cursor just after + // the close of the "-webkit-*gradient(...)" expression. This is the same + // behavior that the other Parse*Gradient functions have in their failure + // cases -- they call "SkipUntil(')') before returning false. So this is + // probably what we want. + nsCOMPtr unprefixingSvc = + do_GetService(NS_CSSUNPREFIXINGSERVICE_CONTRACTID); + NS_ENSURE_TRUE(unprefixingSvc, false); + + bool success; + nsAutoString unprefixedFuncName; + nsAutoString unprefixedFuncBody; + nsresult rv = + unprefixingSvc->GenerateUnprefixedGradientValue(aPrefixedFuncName, + prefixedFuncBody, + unprefixedFuncName, + unprefixedFuncBody, + &success); + + if (NS_FAILED(rv) || !success) { + return false; + } + + // JS service thinks it successfully converted the gradient! Now let's try + // to parse the resulting string. + + // First, add a close-paren if we originally recorded one (so that what we're + // about to put into the CSS parser is a faithful representation of what it + // would've seen if it were just parsing the original input stream): + if (gotCloseParen) { + unprefixedFuncBody.Append(char16_t(')')); + } + + nsAutoScannerChanger scannerChanger(this, unprefixedFuncBody); + if (unprefixedFuncName.EqualsLiteral("linear-gradient")) { + return ParseLinearGradient(aValue, 0); + } + if (unprefixedFuncName.EqualsLiteral("radial-gradient")) { + return ParseRadialGradient(aValue, 0); + } + + NS_ERROR("CSSUnprefixingService returned an unrecognized type of " + "gradient function"); + + return false; +} + +//---------------------------------------------------------------------- + +bool +CSSParserImpl::ParseDeclaration(css::Declaration* aDeclaration, + uint32_t aFlags, + bool aMustCallValueAppended, + bool* aChanged, + nsCSSContextType aContext) +{ + NS_PRECONDITION(aContext == eCSSContext_General || + aContext == eCSSContext_Page, + "Must be page or general context"); + + bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; + + mTempData.AssertInitialState(); + + // Get property name + nsCSSToken* tk = &mToken; + nsAutoString propertyName; + for (;;) { + if (!GetToken(true)) { + if (checkForBraces) { + REPORT_UNEXPECTED_EOF(PEDeclEndEOF); + } + return false; + } + if (eCSSToken_Ident == tk->mType) { + propertyName = tk->mIdent; + // grab the ident before the ExpectSymbol trashes the token + if (!ExpectSymbol(':', true)) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + return false; + } + break; + } + if (tk->IsSymbol(';')) { + // dangling semicolons are skipped + continue; + } + + if (!tk->IsSymbol('}')) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationDeclExpected); + REPORT_UNEXPECTED(PEDeclSkipped); + OUTPUT_ERROR(); + + if (eCSSToken_AtKeyword == tk->mType) { + SkipAtRule(checkForBraces); + return true; // Not a declaration, but don't skip until ';' + } + } + // Not a declaration... + UngetToken(); + return false; + } + + // Don't report property parse errors if we're inside a failing @supports + // rule. + nsAutoSuppressErrors suppressErrors(this, mInFailingSupportsRule); + + // Information about a parsed non-custom property. + nsCSSPropertyID propID; + + // Information about a parsed custom property. + CSSVariableDeclarations::Type variableType; + nsString variableValue; + + // Check if the property name is a custom property. + bool customProperty = nsLayoutUtils::CSSVariablesEnabled() && + nsCSSProps::IsCustomPropertyName(propertyName) && + aContext == eCSSContext_General; + + if (customProperty) { + if (!ParseVariableDeclaration(&variableType, variableValue)) { + REPORT_UNEXPECTED_P(PEValueParsingError, propertyName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + return false; + } + } else { + // Map property name to its ID. + propID = LookupEnabledProperty(propertyName); + if (eCSSProperty_UNKNOWN == propID || + eCSSPropertyExtra_variable == propID || + (aContext == eCSSContext_Page && + !nsCSSProps::PropHasFlags(propID, + CSS_PROPERTY_APPLIES_TO_PAGE_RULE))) { // unknown property + if (NonMozillaVendorIdentifier(propertyName)) { + if (!mInSupportsCondition && + aContext == eCSSContext_General && + !(aFlags & eParseDeclaration_FromUnprefixingSvc) && // no recursion + ShouldUseUnprefixingService()) { + if (ParsePropertyWithUnprefixingService(propertyName, + aDeclaration, aFlags, + aMustCallValueAppended, + aChanged, aContext)) { + return true; + } + } + } else { + REPORT_UNEXPECTED_P(PEUnknownProperty, propertyName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + } + return false; + } + // Then parse the property. + if (!ParseProperty(propID)) { + // XXX Much better to put stuff in the value parsers instead... + REPORT_UNEXPECTED_P(PEValueParsingError, propertyName); + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + mTempData.ClearProperty(propID); + mTempData.AssertInitialState(); + return false; + } + } + + CLEAR_ERROR(); + + // Look for "!important". + PriorityParsingStatus status; + if ((aFlags & eParseDeclaration_AllowImportant) != 0) { + status = ParsePriority(); + } else { + status = ePriority_None; + } + + // Look for a semicolon or close brace. + if (status != ePriority_Error) { + if (!GetToken(true)) { + // EOF is always ok + } else if (mToken.IsSymbol(';')) { + // semicolon is always ok + } else if (mToken.IsSymbol('}')) { + // brace is ok if checkForBraces, but don't eat it + UngetToken(); + if (!checkForBraces) { + status = ePriority_Error; + } + } else { + UngetToken(); + status = ePriority_Error; + } + } + + if (status == ePriority_Error) { + if (checkForBraces) { + REPORT_UNEXPECTED_TOKEN(PEBadDeclOrRuleEnd2); + } else { + REPORT_UNEXPECTED_TOKEN(PEBadDeclEnd); + } + REPORT_UNEXPECTED(PEDeclDropped); + OUTPUT_ERROR(); + if (!customProperty) { + mTempData.ClearProperty(propID); + } + mTempData.AssertInitialState(); + return false; + } + + if (customProperty) { + MOZ_ASSERT(Substring(propertyName, 0, + CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); + // remove '--' + nsDependentString varName(propertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); + aDeclaration->AddVariable(varName, variableType, variableValue, + status == ePriority_Important, false); + } else { + *aChanged |= mData.TransferFromBlock(mTempData, propID, EnabledState(), + status == ePriority_Important, + false, aMustCallValueAppended, + aDeclaration, GetDocument()); + } + + return true; +} + +static const nsCSSPropertyID kBorderTopIDs[] = { + eCSSProperty_border_top_width, + eCSSProperty_border_top_style, + eCSSProperty_border_top_color +}; +static const nsCSSPropertyID kBorderRightIDs[] = { + eCSSProperty_border_right_width, + eCSSProperty_border_right_style, + eCSSProperty_border_right_color +}; +static const nsCSSPropertyID kBorderBottomIDs[] = { + eCSSProperty_border_bottom_width, + eCSSProperty_border_bottom_style, + eCSSProperty_border_bottom_color +}; +static const nsCSSPropertyID kBorderLeftIDs[] = { + eCSSProperty_border_left_width, + eCSSProperty_border_left_style, + eCSSProperty_border_left_color +}; +static const nsCSSPropertyID kBorderInlineStartIDs[] = { + eCSSProperty_border_inline_start_width, + eCSSProperty_border_inline_start_style, + eCSSProperty_border_inline_start_color +}; +static const nsCSSPropertyID kBorderInlineEndIDs[] = { + eCSSProperty_border_inline_end_width, + eCSSProperty_border_inline_end_style, + eCSSProperty_border_inline_end_color +}; +static const nsCSSPropertyID kBorderBlockStartIDs[] = { + eCSSProperty_border_block_start_width, + eCSSProperty_border_block_start_style, + eCSSProperty_border_block_start_color +}; +static const nsCSSPropertyID kBorderBlockEndIDs[] = { + eCSSProperty_border_block_end_width, + eCSSProperty_border_block_end_style, + eCSSProperty_border_block_end_color +}; +static const nsCSSPropertyID kColumnRuleIDs[] = { + eCSSProperty_column_rule_width, + eCSSProperty_column_rule_style, + eCSSProperty_column_rule_color +}; + +bool +CSSParserImpl::ParseEnum(nsCSSValue& aValue, + const KTableEntry aKeywordTable[]) +{ + nsSubstring* ident = NextIdent(); + if (nullptr == ident) { + return false; + } + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident); + if (eCSSKeyword_UNKNOWN < keyword) { + int32_t value; + if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { + aValue.SetIntValue(value, eCSSUnit_Enumerated); + return true; + } + } + + // Put the unknown identifier back and return + UngetToken(); + return false; +} + +bool +CSSParserImpl::ParseAlignEnum(nsCSSValue& aValue, + const KTableEntry aKeywordTable[]) +{ + MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(NS_STYLE_ALIGN_BASELINE, + aKeywordTable) != + eCSSKeyword_UNKNOWN, + "Please use ParseEnum instead"); + nsSubstring* ident = NextIdent(); + if (!ident) { + return false; + } + nsCSSKeyword baselinePrefix = eCSSKeyword_first; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident); + if (keyword == eCSSKeyword_first || keyword == eCSSKeyword_last) { + baselinePrefix = keyword; + ident = NextIdent(); + if (!ident) { + return false; + } + keyword = nsCSSKeywords::LookupKeyword(*ident); + } + if (eCSSKeyword_UNKNOWN < keyword) { + int32_t value; + if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { + if (baselinePrefix == eCSSKeyword_last && + keyword == eCSSKeyword_baseline) { + value = NS_STYLE_ALIGN_LAST_BASELINE; + } + aValue.SetIntValue(value, eCSSUnit_Enumerated); + return true; + } + } + + // Put the unknown identifier back and return + UngetToken(); + return false; +} + +struct UnitInfo { + char name[6]; // needs to be long enough for the longest unit, with + // terminating null. + uint32_t length; + nsCSSUnit unit; + int32_t type; +}; + +#define STR_WITH_LEN(_str) \ + _str, sizeof(_str) - 1 + +const UnitInfo UnitData[] = { + { STR_WITH_LEN("px"), eCSSUnit_Pixel, VARIANT_LENGTH }, + { STR_WITH_LEN("em"), eCSSUnit_EM, VARIANT_LENGTH }, + { STR_WITH_LEN("ex"), eCSSUnit_XHeight, VARIANT_LENGTH }, + { STR_WITH_LEN("pt"), eCSSUnit_Point, VARIANT_LENGTH }, + { STR_WITH_LEN("in"), eCSSUnit_Inch, VARIANT_LENGTH }, + { STR_WITH_LEN("cm"), eCSSUnit_Centimeter, VARIANT_LENGTH }, + { STR_WITH_LEN("ch"), eCSSUnit_Char, VARIANT_LENGTH }, + { STR_WITH_LEN("rem"), eCSSUnit_RootEM, VARIANT_LENGTH }, + { STR_WITH_LEN("mm"), eCSSUnit_Millimeter, VARIANT_LENGTH }, + { STR_WITH_LEN("mozmm"), eCSSUnit_PhysicalMillimeter, VARIANT_LENGTH }, + { STR_WITH_LEN("vw"), eCSSUnit_ViewportWidth, VARIANT_LENGTH }, + { STR_WITH_LEN("vh"), eCSSUnit_ViewportHeight, VARIANT_LENGTH }, + { STR_WITH_LEN("vmin"), eCSSUnit_ViewportMin, VARIANT_LENGTH }, + { STR_WITH_LEN("vmax"), eCSSUnit_ViewportMax, VARIANT_LENGTH }, + { STR_WITH_LEN("pc"), eCSSUnit_Pica, VARIANT_LENGTH }, + { STR_WITH_LEN("q"), eCSSUnit_Quarter, VARIANT_LENGTH }, + { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE }, + { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE }, + { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE }, + { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE }, + { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY }, + { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY }, + { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME }, + { STR_WITH_LEN("ms"), eCSSUnit_Milliseconds, VARIANT_TIME } +}; + +#undef STR_WITH_LEN + +bool +CSSParserImpl::TranslateDimension(nsCSSValue& aValue, + uint32_t aVariantMask, + float aNumber, + const nsString& aUnit) +{ + nsCSSUnit units; + int32_t type = 0; + if (!aUnit.IsEmpty()) { + uint32_t i; + for (i = 0; i < ArrayLength(UnitData); ++i) { + if (aUnit.LowerCaseEqualsASCII(UnitData[i].name, + UnitData[i].length)) { + units = UnitData[i].unit; + type = UnitData[i].type; + break; + } + } + + if (i == ArrayLength(UnitData)) { + // Unknown unit + return false; + } + + if (!mViewportUnitsEnabled && + (eCSSUnit_ViewportWidth == units || + eCSSUnit_ViewportHeight == units || + eCSSUnit_ViewportMin == units || + eCSSUnit_ViewportMax == units)) { + // Viewport units aren't allowed right now, probably because we're + // inside an @page declaration. Fail. + return false; + } + + if ((VARIANT_ABSOLUTE_DIMENSION & aVariantMask) != 0 && + !nsCSSValue::IsPixelLengthUnit(units)) { + return false; + } + } else { + // Must be a zero number... + NS_ASSERTION(0 == aNumber, "numbers without units must be 0"); + if ((VARIANT_LENGTH & aVariantMask) != 0) { + units = eCSSUnit_Pixel; + type = VARIANT_LENGTH; + } + else if ((VARIANT_ANGLE & aVariantMask) != 0) { + NS_ASSERTION(aVariantMask & VARIANT_ZERO_ANGLE, + "must have allowed zero angle"); + units = eCSSUnit_Degree; + type = VARIANT_ANGLE; + } + else { + NS_ERROR("Variant mask does not include dimension; why were we called?"); + return false; + } + } + if ((type & aVariantMask) != 0) { + aValue.SetFloatValue(aNumber, units); + return true; + } + return false; +} + +// Note that this does include VARIANT_CALC, which is numeric. This is +// because calc() parsing, as proposed, drops range restrictions inside +// the calc() expression and clamps the result of the calculation to the +// range. +#define VARIANT_ALL_NONNUMERIC \ + VARIANT_KEYWORD | \ + VARIANT_COLOR | \ + VARIANT_URL | \ + VARIANT_STRING | \ + VARIANT_COUNTER | \ + VARIANT_ATTR | \ + VARIANT_IDENTIFIER | \ + VARIANT_IDENTIFIER_NO_INHERIT | \ + VARIANT_AUTO | \ + VARIANT_INHERIT | \ + VARIANT_NONE | \ + VARIANT_NORMAL | \ + VARIANT_SYSFONT | \ + VARIANT_GRADIENT | \ + VARIANT_TIMING_FUNCTION | \ + VARIANT_ALL | \ + VARIANT_CALC | \ + VARIANT_OPENTYPE_SVG_KEYWORD + +CSSParseResult +CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[], + uint32_t aRestrictions) +{ + switch (aRestrictions) { + default: + MOZ_FALLTHROUGH_ASSERT("should not be reached"); + case 0: + return ParseVariant(aValue, aVariantMask, aKeywordTable); + case CSS_PROPERTY_VALUE_NONNEGATIVE: + return ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable); + case CSS_PROPERTY_VALUE_AT_LEAST_ONE: + return ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable); + } +} + +// Note that callers passing VARIANT_CALC in aVariantMask will get +// full-range parsing inside the calc() expression, and the code that +// computes the calc will be required to clamp the resulting value to an +// appropriate range. +CSSParseResult +CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]) +{ + // The variant mask must only contain non-numeric variants or the ones + // that we specifically handle. + MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC | + VARIANT_NUMBER | + VARIANT_LENGTH | + VARIANT_PERCENT | + VARIANT_INTEGER)) == 0, + "need to update code below to handle additional variants"); + + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); + if (result == CSSParseResult::Ok) { + if (eCSSUnit_Number == aValue.GetUnit() || + aValue.IsLengthUnit()){ + if (aValue.GetFloatValue() < 0) { + UngetToken(); + return CSSParseResult::NotFound; + } + } + else if (aValue.GetUnit() == eCSSUnit_Percent) { + if (aValue.GetPercentValue() < 0) { + UngetToken(); + return CSSParseResult::NotFound; + } + } else if (aValue.GetUnit() == eCSSUnit_Integer) { + if (aValue.GetIntValue() < 0) { + UngetToken(); + return CSSParseResult::NotFound; + } + } + } + return result; +} + +// Note that callers passing VARIANT_CALC in aVariantMask will get +// full-range parsing inside the calc() expression, and the code that +// computes the calc will be required to clamp the resulting value to an +// appropriate range. +CSSParseResult +CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableEntry aKeywordTable[]) +{ + // The variant mask must only contain non-numeric variants or the ones + // that we specifically handle. + MOZ_ASSERT((aVariantMask & ~(VARIANT_ALL_NONNUMERIC | + VARIANT_NUMBER | + VARIANT_INTEGER)) == 0, + "need to update code below to handle additional variants"); + + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); + if (result == CSSParseResult::Ok) { + if (aValue.GetUnit() == eCSSUnit_Integer) { + if (aValue.GetIntValue() < 1) { + UngetToken(); + return CSSParseResult::NotFound; + } + } else if (eCSSUnit_Number == aValue.GetUnit()) { + if (aValue.GetFloatValue() < 1.0f) { + UngetToken(); + return CSSParseResult::NotFound; + } + } + } + return result; +} + +static bool +IsCSSTokenCalcFunction(const nsCSSToken& aToken) +{ + return aToken.mType == eCSSToken_Function && + (aToken.mIdent.LowerCaseEqualsLiteral("calc") || + aToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")); +} + +// Assigns to aValue iff it returns CSSParseResult::Ok. +CSSParseResult +CSSParserImpl::ParseVariant(nsCSSValue& aValue, + uint32_t aVariantMask, + const KTableEntry aKeywordTable[]) +{ + NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) || + !(aVariantMask & VARIANT_NUMBER), + "can't distinguish colors from numbers"); + NS_ASSERTION(!(mHashlessColorQuirk && (aVariantMask & VARIANT_COLOR)) || + !(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)), + "can't distinguish colors from lengths"); + NS_ASSERTION(!(mUnitlessLengthQuirk && (aVariantMask & VARIANT_LENGTH)) || + !(aVariantMask & VARIANT_NUMBER), + "can't distinguish lengths from numbers"); + MOZ_ASSERT(!(aVariantMask & VARIANT_IDENTIFIER) || + !(aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT), + "must not set both VARIANT_IDENTIFIER and " + "VARIANT_IDENTIFIER_NO_INHERIT"); + + uint32_t lineBefore, colBefore; + if (!GetNextTokenLocation(true, &lineBefore, &colBefore) || + !GetToken(true)) { + // Must be at EOF. + return CSSParseResult::NotFound; + } + + nsCSSToken* tk = &mToken; + if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) && + (eCSSToken_Ident == tk->mType)) { + nsCSSKeyword keyword = LookupKeywordPrefixAware(tk->mIdent, + aKeywordTable); + + if (eCSSKeyword_UNKNOWN < keyword) { // known keyword + if ((aVariantMask & VARIANT_AUTO) != 0) { + if (eCSSKeyword_auto == keyword) { + aValue.SetAutoValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_INHERIT) != 0) { + // XXX Should we check IsParsingCompoundProperty, or do all + // callers handle it? (Not all callers set it, though, since + // they want the quirks that are disabled by setting it.) + + // IMPORTANT: If new keywords are added here, + // they probably need to be added in ParseCustomIdent as well. + if (eCSSKeyword_inherit == keyword) { + aValue.SetInheritValue(); + return CSSParseResult::Ok; + } + else if (eCSSKeyword_initial == keyword) { + aValue.SetInitialValue(); + return CSSParseResult::Ok; + } + else if (eCSSKeyword_unset == keyword && + nsLayoutUtils::UnsetValueEnabled()) { + aValue.SetUnsetValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_NONE) != 0) { + if (eCSSKeyword_none == keyword) { + aValue.SetNoneValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_ALL) != 0) { + if (eCSSKeyword_all == keyword) { + aValue.SetAllValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_NORMAL) != 0) { + if (eCSSKeyword_normal == keyword) { + aValue.SetNormalValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_SYSFONT) != 0) { + if (eCSSKeyword__moz_use_system_font == keyword && + !IsParsingCompoundProperty()) { + aValue.SetSystemFontValue(); + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) { + if (sOpentypeSVGEnabled) { + aVariantMask |= VARIANT_KEYWORD; + } + } + if ((aVariantMask & VARIANT_KEYWORD) != 0) { + int32_t value; + if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { + aValue.SetIntValue(value, eCSSUnit_Enumerated); + return CSSParseResult::Ok; + } + } + } + } + // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or + // VARIANT_ZERO_ANGLE. + if (((aVariantMask & VARIANT_NUMBER) != 0) && + (eCSSToken_Number == tk->mType)) { + aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); + return CSSParseResult::Ok; + } + if (((aVariantMask & VARIANT_INTEGER) != 0) && + (eCSSToken_Number == tk->mType) && tk->mIntegerValid) { + aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); + return CSSParseResult::Ok; + } + if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | + VARIANT_FREQUENCY | VARIANT_TIME)) != 0 && + eCSSToken_Dimension == tk->mType) || + ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 && + eCSSToken_Number == tk->mType && + tk->mNumber == 0.0f)) { + if ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 && + tk->mNumber < 0.0) { + UngetToken(); + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; + } + if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) { + return CSSParseResult::Ok; + } + // Put the token back; we didn't parse it, so we shouldn't consume it + UngetToken(); + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; + } + if (((aVariantMask & VARIANT_PERCENT) != 0) && + (eCSSToken_Percentage == tk->mType)) { + aValue.SetPercentValue(tk->mNumber); + return CSSParseResult::Ok; + } + if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px + if (((aVariantMask & VARIANT_LENGTH) != 0) && + (eCSSToken_Number == tk->mType)) { + aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); + return CSSParseResult::Ok; + } + } + + if (IsSVGMode() && !IsParsingCompoundProperty()) { + // STANDARD: SVG Spec states that lengths and coordinates can be unitless + // in which case they default to user-units (1 px = 1 user unit) + if (((aVariantMask & VARIANT_LENGTH) != 0) && + (eCSSToken_Number == tk->mType)) { + aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); + return CSSParseResult::Ok; + } + } + + if (((aVariantMask & VARIANT_URL) != 0) && + eCSSToken_URL == tk->mType) { + SetValueToURL(aValue, tk->mIdent); + return CSSParseResult::Ok; + } + if ((aVariantMask & VARIANT_GRADIENT) != 0 && + eCSSToken_Function == tk->mType) { + // a generated gradient + nsDependentString tmp(tk->mIdent, 0); + uint8_t gradientFlags = 0; + if (sMozGradientsEnabled && + StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) { + tmp.Rebind(tmp, 5); + gradientFlags |= eGradient_MozLegacy; + } else if (sWebkitPrefixedAliasesEnabled && + StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) { + tmp.Rebind(tmp, 8); + gradientFlags |= eGradient_WebkitLegacy; + } + if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) { + tmp.Rebind(tmp, 10); + gradientFlags |= eGradient_Repeating; + } + + if (tmp.LowerCaseEqualsLiteral("linear-gradient")) { + if (!ParseLinearGradient(aValue, gradientFlags)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if (tmp.LowerCaseEqualsLiteral("radial-gradient")) { + if (!ParseRadialGradient(aValue, gradientFlags)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if ((gradientFlags == eGradient_WebkitLegacy) && + tmp.LowerCaseEqualsLiteral("gradient")) { + // Note: we check gradientFlags using '==' to select *exactly* + // eGradient_WebkitLegacy -- and exclude eGradient_Repeating -- because + // we don't want to accept -webkit-repeating-gradient() expressions. + // (This is not a recognized syntax.) + if (!ParseWebkitGradient(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + + if (ShouldUseUnprefixingService() && + !gradientFlags && + StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) { + // Copy 'tmp' into a string on the stack, since as soon as we + // start parsing, its backing store (in "tk") will be overwritten + nsAutoString prefixedFuncName(tmp); + if (!ParseWebkitPrefixedGradientWithService(prefixedFuncName, aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 && + eCSSToken_Function == tk->mType && + tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) { + if (!ParseImageRect(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if ((aVariantMask & VARIANT_ELEMENT) != 0 && + eCSSToken_Function == tk->mType && + tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) { + if (!ParseElement(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if ((aVariantMask & VARIANT_COLOR) != 0) { + if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix + (eCSSToken_ID == tk->mType) || + (eCSSToken_Hash == tk->mType) || + (eCSSToken_Ident == tk->mType) || + ((eCSSToken_Function == tk->mType) && + (tk->mIdent.LowerCaseEqualsLiteral("rgb") || + tk->mIdent.LowerCaseEqualsLiteral("hsl") || + tk->mIdent.LowerCaseEqualsLiteral("rgba") || + tk->mIdent.LowerCaseEqualsLiteral("hsla")))) + { + // Put token back so that parse color can get it + UngetToken(); + return ParseColor(aValue); + } + } + if (((aVariantMask & VARIANT_STRING) != 0) && + (eCSSToken_String == tk->mType)) { + nsAutoString buffer; + buffer.Append(tk->mIdent); + aValue.SetStringValue(buffer, eCSSUnit_String); + return CSSParseResult::Ok; + } + if (((aVariantMask & + (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) && + (eCSSToken_Ident == tk->mType) && + ((aVariantMask & VARIANT_IDENTIFIER) != 0 || + !(tk->mIdent.LowerCaseEqualsLiteral("inherit") || + tk->mIdent.LowerCaseEqualsLiteral("initial") || + (tk->mIdent.LowerCaseEqualsLiteral("unset") && + nsLayoutUtils::UnsetValueEnabled())))) { + aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); + return CSSParseResult::Ok; + } + if (((aVariantMask & VARIANT_COUNTER) != 0) && + (eCSSToken_Function == tk->mType) && + (tk->mIdent.LowerCaseEqualsLiteral("counter") || + tk->mIdent.LowerCaseEqualsLiteral("counters"))) { + if (!ParseCounter(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if (((aVariantMask & VARIANT_ATTR) != 0) && + (eCSSToken_Function == tk->mType) && + tk->mIdent.LowerCaseEqualsLiteral("attr")) { + if (!ParseAttr(aValue)) { + SkipUntil(')'); + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) && + (eCSSToken_Function == tk->mType)) { + if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) { + if (!ParseTransitionTimingFunctionValues(aValue)) { + SkipUntil(')'); + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + if (tk->mIdent.LowerCaseEqualsLiteral("steps")) { + if (!ParseTransitionStepTimingFunctionValues(aValue)) { + SkipUntil(')'); + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + } + if ((aVariantMask & VARIANT_CALC) && + IsCSSTokenCalcFunction(*tk)) { + // calc() currently allows only lengths and percents and number inside it. + // And note that in current implementation, number cannot be mixed with + // length and percent. + if (!ParseCalc(aValue, aVariantMask & VARIANT_LPN)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + + UngetToken(); + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; +} + +bool +CSSParserImpl::ParseCustomIdent(nsCSSValue& aValue, + const nsAutoString& aIdentValue, + const nsCSSKeyword aExcludedKeywords[], + const nsCSSProps::KTableEntry aPropertyKTable[]) +{ + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aIdentValue); + if (keyword == eCSSKeyword_UNKNOWN) { + // Fast path for identifiers that are not known CSS keywords: + aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident); + return true; + } + if (keyword == eCSSKeyword_inherit || + keyword == eCSSKeyword_initial || + keyword == eCSSKeyword_unset || + keyword == eCSSKeyword_default || + (aPropertyKTable && + nsCSSProps::FindIndexOfKeyword(keyword, aPropertyKTable) >= 0)) { + return false; + } + if (aExcludedKeywords) { + for (uint32_t i = 0;; i++) { + nsCSSKeyword excludedKeyword = aExcludedKeywords[i]; + if (excludedKeyword == eCSSKeyword_UNKNOWN) { + break; + } + if (excludedKeyword == keyword) { + return false; + } + } + } + aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident); + return true; +} + +bool +CSSParserImpl::ParseCounter(nsCSSValue& aValue) +{ + nsCSSUnit unit = (mToken.mIdent.LowerCaseEqualsLiteral("counter") ? + eCSSUnit_Counter : eCSSUnit_Counters); + + // A non-iterative for loop to break out when an error occurs. + for (;;) { + if (!GetToken(true)) { + break; + } + if (eCSSToken_Ident != mToken.mType) { + UngetToken(); + break; + } + + RefPtr val = + nsCSSValue::Array::Create(unit == eCSSUnit_Counter ? 2 : 3); + + val->Item(0).SetStringValue(mToken.mIdent, eCSSUnit_Ident); + + if (eCSSUnit_Counters == unit) { + // must have a comma and then a separator string + if (!ExpectSymbol(',', true) || !GetToken(true)) { + break; + } + if (eCSSToken_String != mToken.mType) { + UngetToken(); + break; + } + val->Item(1).SetStringValue(mToken.mIdent, eCSSUnit_String); + } + + // get optional type + int32_t typeItem = eCSSUnit_Counters == unit ? 2 : 1; + nsCSSValue& type = val->Item(typeItem); + if (ExpectSymbol(',', true)) { + if (!ParseCounterStyleNameValue(type) && !ParseSymbols(type)) { + break; + } + } else { + type.SetStringValue(NS_LITERAL_STRING("decimal"), eCSSUnit_Ident); + } + + if (!ExpectSymbol(')', true)) { + break; + } + + aValue.SetArrayValue(val, unit); + return true; + } + + SkipUntil(')'); + return false; +} + +bool +CSSParserImpl::ParseAttr(nsCSSValue& aValue) +{ + if (!GetToken(true)) { + return false; + } + + nsAutoString attr; + if (eCSSToken_Ident == mToken.mType) { // attr name or namespace + nsAutoString holdIdent(mToken.mIdent); + if (ExpectSymbol('|', false)) { // namespace + int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent); + if (nameSpaceID == kNameSpaceID_Unknown) { + return false; + } + attr.AppendInt(nameSpaceID, 10); + attr.Append(char16_t('|')); + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return false; + } + if (eCSSToken_Ident == mToken.mType) { + attr.Append(mToken.mIdent); + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return false; + } + } + else { // no namespace + attr = holdIdent; + } + } + else if (mToken.IsSymbol('*')) { // namespace wildcard + // Wildcard namespace makes no sense here and is not allowed + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return false; + } + else if (mToken.IsSymbol('|')) { // explicit NO namespace + if (! GetToken(false)) { + REPORT_UNEXPECTED_EOF(PEAttributeNameEOF); + return false; + } + if (eCSSToken_Ident == mToken.mType) { + attr.Append(mToken.mIdent); + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameExpected); + UngetToken(); + return false; + } + } + else { + REPORT_UNEXPECTED_TOKEN(PEAttributeNameOrNamespaceExpected); + UngetToken(); + return false; + } + if (!ExpectSymbol(')', true)) { + return false; + } + aValue.SetStringValue(attr, eCSSUnit_Attr); + return true; +} + +bool +CSSParserImpl::ParseSymbols(nsCSSValue& aValue) +{ + if (!GetToken(true)) { + return false; + } + if (mToken.mType != eCSSToken_Function && + !mToken.mIdent.LowerCaseEqualsLiteral("symbols")) { + UngetToken(); + return false; + } + + RefPtr params = nsCSSValue::Array::Create(2); + nsCSSValue& type = params->Item(0); + nsCSSValue& symbols = params->Item(1); + + if (!ParseEnum(type, nsCSSProps::kCounterSymbolsSystemKTable)) { + type.SetIntValue(NS_STYLE_COUNTER_SYSTEM_SYMBOLIC, eCSSUnit_Enumerated); + } + + bool first = true; + nsCSSValueList* item = symbols.SetListValue(); + for (;;) { + // FIXME Should also include VARIANT_IMAGE. See bug 1071436. + if (!ParseSingleTokenVariant(item->mValue, VARIANT_STRING, nullptr)) { + break; + } + if (ExpectSymbol(')', true)) { + if (first) { + switch (type.GetIntValue()) { + case NS_STYLE_COUNTER_SYSTEM_NUMERIC: + case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC: + // require at least two symbols + return false; + } + } + aValue.SetArrayValue(params, eCSSUnit_Symbols); + return true; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + first = false; + } + + SkipUntil(')'); + return false; +} + +bool +CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL) +{ + if (!mSheetPrincipal) { + if (!mSheetPrincipalRequired) { + /* Pretend to succeed. */ + return true; + } + + NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an " + "origin principal"); + return false; + } + + RefPtr buffer(nsCSSValue::BufferFromString(aURL)); + + // Note: urlVal retains its own reference to |buffer|. + mozilla::css::URLValue *urlVal = + new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal); + aValue.SetURLValue(urlVal); + return true; +} + +/** + * Parse the image-orientation property, which has the grammar: + * flip? | flip | from-image + */ +bool +CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { + // 'inherit', 'initial' and 'unset' must be alone + return true; + } + + // Check for an angle with optional 'flip'. + nsCSSValue angle; + if (ParseSingleTokenVariant(angle, VARIANT_ANGLE, nullptr)) { + nsCSSValue flip; + + if (ParseSingleTokenVariant(flip, VARIANT_KEYWORD, + nsCSSProps::kImageOrientationFlipKTable)) { + RefPtr array = nsCSSValue::Array::Create(2); + array->Item(0) = angle; + array->Item(1) = flip; + aValue.SetArrayValue(array, eCSSUnit_Array); + } else { + aValue = angle; + } + + return true; + } + + // The remaining possibilities (bare 'flip' and 'from-image') are both + // keywords, so we can handle them at the same time. + nsCSSValue keyword; + if (ParseSingleTokenVariant(keyword, VARIANT_KEYWORD, + nsCSSProps::kImageOrientationKTable)) { + aValue = keyword; + return true; + } + + // All possibilities failed. + return false; +} + +/** + * Parse the arguments of -moz-image-rect() function. + * -moz-image-rect(, , , , ) + */ +bool +CSSParserImpl::ParseImageRect(nsCSSValue& aImage) +{ + // A non-iterative for loop to break out when an error occurs. + for (;;) { + nsCSSValue newFunction; + static const uint32_t kNumArgs = 5; + nsCSSValue::Array* func = + newFunction.InitFunction(eCSSKeyword__moz_image_rect, kNumArgs); + + // func->Item(0) is reserved for the function name. + nsCSSValue& url = func->Item(1); + nsCSSValue& top = func->Item(2); + nsCSSValue& right = func->Item(3); + nsCSSValue& bottom = func->Item(4); + nsCSSValue& left = func->Item(5); + + nsAutoString urlString; + if (!ParseURLOrString(urlString) || + !SetValueToURL(url, urlString) || + !ExpectSymbol(',', true)) { + break; + } + + static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT; + if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) || + !ExpectSymbol(',', true) || + !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) || + !ExpectSymbol(',', true) || + !ParseSingleTokenNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) || + !ExpectSymbol(',', true) || + !ParseSingleTokenNonNegativeVariant(left, VARIANT_SIDE, nullptr) || + !ExpectSymbol(')', true)) + break; + + aImage = newFunction; + return true; + } + + SkipUntil(')'); + return false; +} + +// : -moz-element(# ) +bool +CSSParserImpl::ParseElement(nsCSSValue& aValue) +{ + // A non-iterative for loop to break out when an error occurs. + for (;;) { + if (!GetToken(true)) + break; + + if (mToken.mType == eCSSToken_ID) { + aValue.SetStringValue(mToken.mIdent, eCSSUnit_Element); + } else { + UngetToken(); + break; + } + + if (!ExpectSymbol(')', true)) + break; + + return true; + } + + // If we detect a syntax error, we must match the opening parenthesis of the + // function with the closing parenthesis and skip all the tokens in between. + SkipUntil(')'); + return false; +} + +// flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] +bool +CSSParserImpl::ParseFlex() +{ + // First check for inherit / initial / unset + nsCSSValue tmpVal; + if (ParseSingleTokenVariant(tmpVal, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_flex_grow, tmpVal); + AppendValue(eCSSProperty_flex_shrink, tmpVal); + AppendValue(eCSSProperty_flex_basis, tmpVal); + return true; + } + + // Next, check for 'none' == '0 0 auto' + if (ParseSingleTokenVariant(tmpVal, VARIANT_NONE, nullptr)) { + AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto)); + return true; + } + + // OK, try parsing our value as individual per-subproperty components: + // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] + + // Each subproperty has a default value that it takes when it's omitted in a + // "flex" shorthand value. These default values are *only* for the shorthand + // syntax -- they're distinct from the subproperties' own initial values. We + // start with each subproperty at its default, as if we had "flex: 1 1 0%". + nsCSSValue flexGrow(1.0f, eCSSUnit_Number); + nsCSSValue flexShrink(1.0f, eCSSUnit_Number); + nsCSSValue flexBasis(0.0f, eCSSUnit_Percent); + + // OVERVIEW OF PARSING STRATEGY: + // ============================= + // a) Parse the first component as either flex-basis or flex-grow. + // b) If it wasn't flex-grow, parse the _next_ component as flex-grow. + // c) Now we've just parsed flex-grow -- so try parsing the next thing as + // flex-shrink. + // d) Finally: If we didn't get flex-basis at the beginning, try to parse + // it now, at the end. + // + // More details in each section below. + + uint32_t flexBasisVariantMask = + (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT)); + + // (a) Parse first component. It can be either be a 'flex-basis' value or a + // 'flex-grow' value, so we use the flex-basis-specific variant mask, along + // with VARIANT_NUMBER to accept 'flex-grow' values. + // + // NOTE: if we encounter unitless 0 here, we *must* interpret it as a + // 'flex-grow' value (a number), *not* as a 'flex-basis' value (a length). + // Conveniently, that's the behavior this combined variant-mask gives us -- + // it'll treat unitless 0 as a number. The flexbox spec requires this: + // "a unitless zero that is not already preceded by two flex factors must be + // interpreted as a flex factor. + if (ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER, + nsCSSProps::kWidthKTable) != CSSParseResult::Ok) { + // First component was not a valid flex-basis or flex-grow value. Fail. + return false; + } + + // Record what we just parsed as either flex-basis or flex-grow: + bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number); + (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal; + + // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow. + bool doneParsing = false; + if (wasFirstComponentFlexBasis) { + if (ParseNonNegativeNumber(tmpVal)) { + flexGrow = tmpVal; + } else { + // Failed to parse anything after our flex-basis -- that's fine. We can + // skip the remaining parsing. + doneParsing = true; + } + } + + if (!doneParsing) { + // (c) OK -- the last thing we parsed was flex-grow, so look for a + // flex-shrink in the next position. + if (ParseNonNegativeNumber(tmpVal)) { + flexShrink = tmpVal; + } + + // d) Finally: If we didn't get flex-basis at the beginning, try to parse + // it now, at the end. + // + // NOTE: If we encounter unitless 0 in this final position, we'll parse it + // as a 'flex-basis' value. That's OK, because we know it must have + // been "preceded by 2 flex factors" (justification below), which gets us + // out of the spec's requirement of otherwise having to treat unitless 0 + // as a flex factor. + // + // JUSTIFICATION: How do we know that a unitless 0 here must have been + // preceded by 2 flex factors? Well, suppose we had a unitless 0 that + // was preceded by only 1 flex factor. Then, we would have already + // accepted this unitless 0 as the 'flex-shrink' value, up above (since + // it's a valid flex-shrink value), and we'd have moved on to the next + // token (if any). And of course, if we instead had a unitless 0 preceded + // by *no* flex factors (if it were the first token), we would've already + // parsed it in our very first call to ParseNonNegativeVariant(). So, any + // unitless 0 encountered here *must* have been preceded by 2 flex factors. + if (!wasFirstComponentFlexBasis) { + CSSParseResult result = + ParseNonNegativeVariant(tmpVal, flexBasisVariantMask, + nsCSSProps::kWidthKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + flexBasis = tmpVal; + } + } + } + + AppendValue(eCSSProperty_flex_grow, flexGrow); + AppendValue(eCSSProperty_flex_shrink, flexShrink); + AppendValue(eCSSProperty_flex_basis, flexBasis); + + return true; +} + +// flex-flow: || +bool +CSSParserImpl::ParseFlexFlow() +{ + static const nsCSSPropertyID kFlexFlowSubprops[] = { + eCSSProperty_flex_direction, + eCSSProperty_flex_wrap + }; + const size_t numProps = MOZ_ARRAY_LENGTH(kFlexFlowSubprops); + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, kFlexFlowSubprops, numProps); + + // Bail if we didn't successfully parse anything + if (found < 1) { + return false; + } + + // If either property didn't get an explicit value, use its initial value. + if ((found & 1) == 0) { + values[0].SetIntValue(NS_STYLE_FLEX_DIRECTION_ROW, eCSSUnit_Enumerated); + } + if ((found & 2) == 0) { + values[1].SetIntValue(NS_STYLE_FLEX_WRAP_NOWRAP, eCSSUnit_Enumerated); + } + + // Store these values and declare success! + for (size_t i = 0; i < numProps; i++) { + AppendValue(kFlexFlowSubprops[i], values[i]); + } + return true; +} + +bool +CSSParserImpl::ParseGridAutoFlow() +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_grid_auto_flow, value); + return true; + } + + static const int32_t mask[] = { + NS_STYLE_GRID_AUTO_FLOW_ROW | NS_STYLE_GRID_AUTO_FLOW_COLUMN, + MASK_END_VALUE + }; + if (!ParseBitmaskValues(value, nsCSSProps::kGridAutoFlowKTable, mask)) { + return false; + } + int32_t bitField = value.GetIntValue(); + + // If neither row nor column is provided, row is assumed. + if (!(bitField & (NS_STYLE_GRID_AUTO_FLOW_ROW | + NS_STYLE_GRID_AUTO_FLOW_COLUMN))) { + value.SetIntValue(bitField | NS_STYLE_GRID_AUTO_FLOW_ROW, + eCSSUnit_Enumerated); + } + + AppendValue(eCSSProperty_grid_auto_flow, value); + return true; +} + +static const nsCSSKeyword kGridLineKeywords[] = { + eCSSKeyword_span, + eCSSKeyword_UNKNOWN // End-of-array marker +}; + +CSSParseResult +CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue) +{ + if (!ExpectSymbol('[', true)) { + return CSSParseResult::NotFound; + } + if (!GetToken(true) || mToken.IsSymbol(']')) { + return CSSParseResult::Ok; + } + // 'return' so far leaves aValue untouched, to represent an empty list. + + nsCSSValueList* item; + if (aValue.GetUnit() == eCSSUnit_List) { + // Find the end of an existing list. + // The grid-template shorthand uses this, at most once for a given list. + + // NOTE: we could avoid this traversal by somehow keeping around + // a pointer to the last item from the previous call. + // It's not yet clear if this is worth the additional code complexity. + item = aValue.GetListValue(); + while (item->mNext) { + item = item->mNext; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + } else { + MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "Unexpected unit"); + item = aValue.SetListValue(); + } + for (;;) { + if (!(eCSSToken_Ident == mToken.mType && + ParseCustomIdent(item->mValue, mToken.mIdent, kGridLineKeywords))) { + UngetToken(); + SkipUntil(']'); + return CSSParseResult::Error; + } + if (!GetToken(true) || mToken.IsSymbol(']')) { + return CSSParseResult::Ok; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + } +} + +// Assuming the 'repeat(' function token has already been consumed, +// parse the rest of repeat( | auto-fill, +) +// Append to the linked list whose end is given by |aTailPtr|, +// and update |aTailPtr| to point to the new end of the list. +bool +CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr) +{ + int32_t repetitions; + Maybe repeatAutoEnum; + if (!ParseGridTrackRepeatIntro(true, &repetitions, &repeatAutoEnum)) { + return false; + } + if (repeatAutoEnum.isSome()) { + // Parse exactly one . + nsCSSValue listValue; + nsCSSValueList* list = listValue.SetListValue(); + if (ParseGridLineNames(list->mValue) != CSSParseResult::Ok) { + return false; + } + if (!ExpectSymbol(')', true)) { + return false; + } + // Instead of hooking up this list into the flat name list as usual, + // we create a pair(Int, List) where the first value is the auto-fill + // keyword and the second is the name list to repeat. + nsCSSValue kwd; + kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated); + *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList; + (*aTailPtr)->mValue.SetPairValue(kwd, listValue); + return true; + } + + // Parse at least one + nsCSSValueList* tail = *aTailPtr; + do { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) { + return false; + } + } while (!ExpectSymbol(')', true)); + nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext; + nsCSSValueList* lastRepeatedItem = tail; + + // Our repeated items are already in the target list once, + // so they need to be repeated |repetitions - 1| more times. + MOZ_ASSERT(repetitions > 0, "Expected positive repetitions"); + while (--repetitions) { + nsCSSValueList* repeatedItem = firstRepeatedItem; + for (;;) { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + tail->mValue = repeatedItem->mValue; + if (repeatedItem == lastRepeatedItem) { + break; + } + repeatedItem = repeatedItem->mNext; + } + } + *aTailPtr = tail; + return true; +} + +// Assuming a 'subgrid' keyword was already consumed, parse ? +bool +CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue) +{ + nsCSSValueList* item = aValue.SetListValue(); + // This marker distinguishes the value from a . + item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID, + eCSSUnit_Enumerated); + bool haveRepeatAuto = false; + for (;;) { + // First try to parse , i.e. + // repeat( | auto-fill, +) + if (!GetToken(true)) { + return true; + } + if (mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("repeat")) { + nsCSSValueList* startOfRepeat = item; + if (!ParseGridLineNameListRepeat(&item)) { + SkipUntil(')'); + return false; + } + if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) { + if (haveRepeatAuto) { + REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillInNameList); + return false; + } + haveRepeatAuto = true; + } + } else { + UngetToken(); + + // This was not a repeat() function. Try to parse . + nsCSSValue lineNames; + CSSParseResult result = ParseGridLineNames(lineNames); + if (result == CSSParseResult::NotFound) { + return true; + } + if (result == CSSParseResult::Error) { + return false; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + item->mValue = lineNames; + } + } +} + +CSSParseResult +CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue) +{ + CSSParseResult result = ParseNonNegativeVariant(aValue, + VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD, + nsCSSProps::kGridTrackBreadthKTable); + + if (result == CSSParseResult::Ok || + result == CSSParseResult::Error) { + return result; + } + + // Attempt to parse (a dimension with the "fr" unit). + if (!GetToken(true)) { + return CSSParseResult::NotFound; + } + if (!(eCSSToken_Dimension == mToken.mType && + mToken.mIdent.LowerCaseEqualsLiteral("fr") && + mToken.mNumber >= 0)) { + UngetToken(); + return CSSParseResult::NotFound; + } + aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction); + return CSSParseResult::Ok; +} + +// Parse a , or when aFlags has eFixedTrackSize. +CSSParseResult +CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue, + GridTrackSizeFlags aFlags) +{ + const bool requireFixedSize = + !!(aFlags & GridTrackSizeFlags::eFixedTrackSize); + // Attempt to parse a single . + CSSParseResult result = ParseGridTrackBreadth(aValue); + if (requireFixedSize && result == CSSParseResult::Ok && + !aValue.IsLengthPercentCalcUnit()) { + result = CSSParseResult::Error; + } + if (result == CSSParseResult::Error) { + return result; + } + if (result == CSSParseResult::Ok) { + if (aValue.GetUnit() == eCSSUnit_FlexFraction) { + // Single value is represented internally as minmax(auto, ). + nsCSSValue minmax; + nsCSSValue::Array* func = minmax.InitFunction(eCSSKeyword_minmax, 2); + func->Item(1).SetAutoValue(); + func->Item(2) = aValue; + aValue = minmax; + } + return result; + } + + // Attempt to parse a minmax() or fit-content() function. + if (!GetToken(true)) { + return CSSParseResult::NotFound; + } + if (eCSSToken_Function != mToken.mType) { + UngetToken(); + return CSSParseResult::NotFound; + } + if (mToken.mIdent.LowerCaseEqualsLiteral("fit-content")) { + nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_fit_content, 1); + if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok && + func->Item(1).IsLengthPercentCalcUnit() && + ExpectSymbol(')', true)) { + return CSSParseResult::Ok; + } + SkipUntil(')'); + return CSSParseResult::Error; + } + if (!mToken.mIdent.LowerCaseEqualsLiteral("minmax")) { + UngetToken(); + return CSSParseResult::NotFound; + } + nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2); + if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok && + ExpectSymbol(',', true) && + ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok && + ExpectSymbol(')', true)) { + if (requireFixedSize && + !func->Item(1).IsLengthPercentCalcUnit() && + !func->Item(2).IsLengthPercentCalcUnit()) { + return CSSParseResult::Error; + } + // Reject min-sizing. + if (func->Item(1).GetUnit() == eCSSUnit_FlexFraction) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; + } + SkipUntil(')'); + return CSSParseResult::Error; +} + +bool +CSSParserImpl::ParseGridAutoColumnsRows(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) || + ParseGridTrackSize(value) == CSSParseResult::Ok) { + AppendValue(aPropID, value); + return true; + } + return false; +} + +bool +CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue, + const nsCSSValue& aFirstLineNames, + GridTrackListFlags aFlags) +{ + nsCSSValueList* firstLineNamesItem = aValue.SetListValue(); + firstLineNamesItem->mValue = aFirstLineNames; + + // This function is trying to parse , which is + // [ ? [ | ] ]+ ? + // and we're already past the first "?". + // If aFlags contains eExplicitTrackList then is disallowed. + // + // Each iteration of the following loop attempts to parse either a + // repeat() or a expression, and then an (optional) + // expression. + // + // The only successful exit point from this loop is the ::NotFound + // case after ParseGridTrackSize(); i.e. we'll greedily parse + // repeat()/ until we can't find one. + nsCSSValueList* item = firstLineNamesItem; + bool haveRepeatAuto = false; + for (;;) { + // First try to parse repeat() + if (!GetToken(true)) { + break; + } + if (!(aFlags & GridTrackListFlags::eExplicitTrackList) && + mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("repeat")) { + nsCSSValueList* startOfRepeat = item; + if (!ParseGridTrackListRepeat(&item)) { + SkipUntil(')'); + return false; + } + auto firstRepeat = startOfRepeat->mNext; + if (firstRepeat->mValue.GetUnit() == eCSSUnit_Pair) { + if (haveRepeatAuto) { + REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillFitInTrackList); + return false; + } + haveRepeatAuto = true; + // We're parsing an , which requires that all tracks + // are , so we need to check the ones we've parsed already. + for (nsCSSValueList* list = firstLineNamesItem->mNext; + list != firstRepeat; list = list->mNext) { + if (list->mValue.GetUnit() == eCSSUnit_Function) { + nsCSSValue::Array* func = list->mValue.GetArrayValue(); + auto funcName = func->Item(0).GetKeywordValue(); + if (funcName == eCSSKeyword_minmax) { + if (!func->Item(1).IsLengthPercentCalcUnit() && + !func->Item(2).IsLengthPercentCalcUnit()) { + return false; + } + } else { + MOZ_ASSERT(funcName == eCSSKeyword_fit_content, + "Expected minmax() or fit-content() function"); + return false; // fit-content() is not a + } + } else if (!list->mValue.IsLengthPercentCalcUnit()) { + return false; + } + list = list->mNext; // skip line names + } + } + } else { + UngetToken(); + + // Not a repeat() function; try to parse | . + nsCSSValue trackSize; + GridTrackSizeFlags flags = haveRepeatAuto + ? GridTrackSizeFlags::eFixedTrackSize + : GridTrackSizeFlags::eDefaultTrackSize; + CSSParseResult result = ParseGridTrackSize(trackSize, flags); + if (result == CSSParseResult::Error) { + return false; + } + if (result == CSSParseResult::NotFound) { + // What we've parsed so far is a valid + // (modulo the "at least one " check below.) + // Stop here. + break; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + item->mValue = trackSize; + + item->mNext = new nsCSSValueList; + item = item->mNext; + } + if (ParseGridLineNames(item->mValue) == CSSParseResult::Error) { + return false; + } + } + + // Require at least one . + if (item == firstLineNamesItem) { + return false; + } + + MOZ_ASSERT(aValue.GetListValue() && + aValue.GetListValue()->mNext && + aValue.GetListValue()->mNext->mNext, + " should have a minimum length of 3"); + return true; +} + +// Takes ownership of |aSecond| +static void +ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond) +{ + if (aSecond.GetUnit() == eCSSUnit_Null) { + // Nothing to do. + return; + } + if (aFirst.GetUnit() == eCSSUnit_Null) { + // Empty or omitted . Replace it. + aFirst = aSecond; + return; + } + + // Join the two lists. + nsCSSValueList* source = aSecond.GetListValue(); + nsCSSValueList* target = aFirst.GetListValue(); + // Find the end: + while (target->mNext) { + target = target->mNext; + } + // Copy the first name. We can't take ownership of it + // as it'll be destroyed when |aSecond| goes out of scope. + target->mNext = new nsCSSValueList; + target = target->mNext; + target->mValue = source->mValue; + // Move the rest of the linked list. + target->mNext = source->mNext; + source->mNext = nullptr; +} + +// Assuming the 'repeat(' function token has already been consumed, +// parse "repeat( | auto-fill | auto-fit ," +// (or "repeat( | auto-fill ," when aForSubgrid is true) +// and stop after the comma. Return true when parsing succeeds, +// with aRepetitions set to the number of repetitions and aRepeatAutoEnum set +// to an enum value for auto-fill | auto-fit (it's not set at all when +// was parsed). +bool +CSSParserImpl::ParseGridTrackRepeatIntro(bool aForSubgrid, + int32_t* aRepetitions, + Maybe* aRepeatAutoEnum) +{ + if (!GetToken(true)) { + return false; + } + if (mToken.mType == eCSSToken_Ident) { + if (mToken.mIdent.LowerCaseEqualsLiteral("auto-fill")) { + aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FILL); + } else if (!aForSubgrid && + mToken.mIdent.LowerCaseEqualsLiteral("auto-fit")) { + aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FIT); + } else { + return false; + } + *aRepetitions = 1; + } else if (mToken.mType == eCSSToken_Number) { + if (!(mToken.mIntegerValid && + mToken.mInteger > 0)) { + return false; + } + *aRepetitions = std::min(mToken.mInteger, GRID_TEMPLATE_MAX_REPETITIONS); + } else { + return false; + } + + if (!ExpectSymbol(',', true)) { + return false; + } + return true; +} + +// Assuming the 'repeat(' function token has already been consumed, +// parse the rest of +// repeat( | auto-fill | auto-fit , +// [ ? ]+ ? ) +// Append to the linked list whose end is given by |aTailPtr|, +// and update |aTailPtr| to point to the new end of the list. +// Note: only one is allowed for auto-fill/fit +bool +CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr) +{ + int32_t repetitions; + Maybe repeatAutoEnum; + if (!ParseGridTrackRepeatIntro(false, &repetitions, &repeatAutoEnum)) { + return false; + } + + // Parse [ ? ]+ ? + // but keep the first and last separate + // because they'll need to be joined. + // http://dev.w3.org/csswg/css-grid/#repeat-notation + nsCSSValue firstLineNames; + nsCSSValue trackSize; + nsCSSValue lastLineNames; + // Optional + if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) { + return false; + } + // Required + GridTrackSizeFlags flags = repeatAutoEnum.isSome() + ? GridTrackSizeFlags::eFixedTrackSize + : GridTrackSizeFlags::eDefaultTrackSize; + if (ParseGridTrackSize(trackSize, flags) != CSSParseResult::Ok) { + return false; + } + // Use nsAutoPtr to free the list in case of early return. + nsAutoPtr firstTrackSizeItemAuto(new nsCSSValueList); + firstTrackSizeItemAuto->mValue = trackSize; + + nsCSSValueList* item = firstTrackSizeItemAuto; + for (;;) { + // Optional + if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) { + return false; + } + + if (ExpectSymbol(')', true)) { + break; + } + + // only accepts a single track size: + // ? ? + if (repeatAutoEnum.isSome()) { + REPORT_UNEXPECTED(PEMoreThanOneGridRepeatTrackSize); + return false; + } + + // Required + if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) { + return false; + } + + item->mNext = new nsCSSValueList; + item = item->mNext; + item->mValue = lastLineNames; + // Do not append to this list at the next iteration. + lastLineNames.Reset(); + + item->mNext = new nsCSSValueList; + item = item->mNext; + item->mValue = trackSize; + } + nsCSSValueList* lastTrackSizeItem = item; + + // [ ? ]+ ? is now parsed into: + // * firstLineNames: the first + // * a linked list of odd length >= 1, from firstTrackSizeItem + // (the first ) to lastTrackSizeItem (the last), + // with the sublists in between + // * lastLineNames: the last + + if (repeatAutoEnum.isSome()) { + // Instead of hooking up this list into the flat track/name list as usual, + // we create a pair(Int, List) where the first value is the auto-fill/fit + // keyword and the second is the list to repeat. There are three items + // in this list, the first is the list of line names before the track size, + // the second item is the track size, and the last item is the list of line + // names after the track size. Note that the line names are NOT merged + // with any line names before/after the repeat() itself. + nsCSSValue listValue; + nsCSSValueList* list = listValue.SetListValue(); + list->mValue = firstLineNames; + list = list->mNext = new nsCSSValueList; + list->mValue = trackSize; + list = list->mNext = new nsCSSValueList; + list->mValue = lastLineNames; + nsCSSValue kwd; + kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated); + *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList; + (*aTailPtr)->mValue.SetPairValue(kwd, listValue); + // Append an empty list since the caller expects that to represent the names + // that follows the repeat() function. + *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList; + return true; + } + + // Join the last and first (in that order.) + // For example, repeat(3, (a) 100px (b) 200px (c)) results in + // (a) 100px (b) 200px (c a) 100px (b) 200px (c a) 100px (b) 200px (c) + // This is (c a). + // Make deep copies: the originals will be moved. + nsCSSValue joinerLineNames; + { + nsCSSValueList* target = nullptr; + if (lastLineNames.GetUnit() != eCSSUnit_Null) { + target = joinerLineNames.SetListValue(); + nsCSSValueList* source = lastLineNames.GetListValue(); + for (;;) { + target->mValue = source->mValue; + source = source->mNext; + if (!source) { + break; + } + target->mNext = new nsCSSValueList; + target = target->mNext; + } + } + + if (firstLineNames.GetUnit() != eCSSUnit_Null) { + if (target) { + target->mNext = new nsCSSValueList; + target = target->mNext; + } else { + target = joinerLineNames.SetListValue(); + } + nsCSSValueList* source = firstLineNames.GetListValue(); + for (;;) { + target->mValue = source->mValue; + source = source->mNext; + if (!source) { + break; + } + target->mNext = new nsCSSValueList; + target = target->mNext; + } + } + } + + // Join our first with the one before repeat(). + // (a) repeat(1, (b) 20px) expands to (a b) 20px + nsCSSValueList* previousItemBeforeRepeat = *aTailPtr; + ConcatLineNames(previousItemBeforeRepeat->mValue, firstLineNames); + + // Move our linked list + // (first to last , with the sublists in between). + // This is the first repetition. + NS_ASSERTION(previousItemBeforeRepeat->mNext == nullptr, + "Expected the end of a linked list"); + previousItemBeforeRepeat->mNext = firstTrackSizeItemAuto.forget(); + nsCSSValueList* firstTrackSizeItem = previousItemBeforeRepeat->mNext; + nsCSSValueList* tail = lastTrackSizeItem; + + // Repeat |repetitions - 1| more times: + // * the joiner + // * the linked list + // (first to last , with the sublists in between) + MOZ_ASSERT(repetitions > 0, "Expected positive repetitions"); + while (--repetitions) { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + tail->mValue = joinerLineNames; + + nsCSSValueList* repeatedItem = firstTrackSizeItem; + for (;;) { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + tail->mValue = repeatedItem->mValue; + if (repeatedItem == lastTrackSizeItem) { + break; + } + repeatedItem = repeatedItem->mNext; + } + } + + // Finally, move our last . + // Any immediately after repeat() will append to it. + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + tail->mValue = lastLineNames; + + *aTailPtr = tail; + return true; +} + +bool +CSSParserImpl::ParseGridTrackList(nsCSSPropertyID aPropID, + GridTrackListFlags aFlags) +{ + nsCSSValue value; + nsCSSValue firstLineNames; + if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error || + !ParseGridTrackListWithFirstLineNames(value, firstLineNames, aFlags)) { + return false; + } + AppendValue(aPropID, value); + return true; +} + +bool +CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + AppendValue(aPropID, value); + return true; + } + + nsSubstring* ident = NextIdent(); + if (ident) { + if (ident->LowerCaseEqualsLiteral("subgrid")) { + if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) { + REPORT_UNEXPECTED(PESubgridNotSupported); + return false; + } + if (!ParseOptionalLineNameListAfterSubgrid(value)) { + return false; + } + AppendValue(aPropID, value); + return true; + } + UngetToken(); + } + + return ParseGridTrackList(aPropID); +} + +bool +CSSParserImpl::ParseGridTemplateAreasLine(const nsAutoString& aInput, + css::GridTemplateAreasValue* aAreas, + nsDataHashtable& aAreaIndices) +{ + aAreas->mTemplates.AppendElement(mToken.mIdent); + + nsCSSGridTemplateAreaScanner scanner(aInput); + nsCSSGridTemplateAreaToken token; + css::GridNamedArea* currentArea = nullptr; + uint32_t row = aAreas->NRows(); + // Column numbers starts at 1, but we might not have any, eg + // grid-template-areas:""; which will result in mNColumns == 0. + uint32_t column = 0; + while (scanner.Next(token)) { + ++column; + if (token.isTrash) { + return false; + } + if (currentArea) { + if (token.mName == currentArea->mName) { + if (currentArea->mRowStart == row) { + // Next column in the first row of this named area. + currentArea->mColumnEnd++; + } + continue; + } + // We're exiting |currentArea|, so currentArea is ending at |column|. + // Make sure that this is consistent with currentArea on previous rows: + if (currentArea->mColumnEnd != column) { + NS_ASSERTION(currentArea->mRowStart != row, + "Inconsistent column end for the first row of a named area."); + // Not a rectangle + return false; + } + currentArea = nullptr; + } + if (!token.mName.IsEmpty()) { + // Named cell that doesn't have a cell with the same name on its left. + + // Check if this is the continuation of an existing named area: + uint32_t index; + if (aAreaIndices.Get(token.mName, &index)) { + MOZ_ASSERT(index < aAreas->mNamedAreas.Length(), + "Invalid aAreaIndices hash table"); + currentArea = &aAreas->mNamedAreas[index]; + if (currentArea->mColumnStart != column || + currentArea->mRowEnd != row) { + // Existing named area, but not forming a rectangle + return false; + } + // Next row of an existing named area + currentArea->mRowEnd++; + } else { + // New named area + aAreaIndices.Put(token.mName, aAreas->mNamedAreas.Length()); + currentArea = aAreas->mNamedAreas.AppendElement(); + currentArea->mName = token.mName; + // For column or row N (starting at 1), + // the start line is N, the end line is N + 1 + currentArea->mColumnStart = column; + currentArea->mColumnEnd = column + 1; + currentArea->mRowStart = row; + currentArea->mRowEnd = row + 1; + } + } + } + if (currentArea && currentArea->mColumnEnd != column + 1) { + NS_ASSERTION(currentArea->mRowStart != row, + "Inconsistent column end for the first row of a named area."); + // Not a rectangle + return false; + } + + // On the first row, set the number of columns + // that grid-template-areas contributes to the explicit grid. + // On other rows, check that the number of columns is consistent + // between rows. + if (row == 1) { + aAreas->mNColumns = column; + } else if (aAreas->mNColumns != column) { + return false; + } + return true; +} + +bool +CSSParserImpl::ParseGridTemplateAreas() +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + AppendValue(eCSSProperty_grid_template_areas, value); + return true; + } + + RefPtr areas = + new css::GridTemplateAreasValue(); + nsDataHashtable areaIndices; + for (;;) { + if (!GetToken(true)) { + break; + } + if (eCSSToken_String != mToken.mType) { + UngetToken(); + break; + } + if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) { + return false; + } + } + + if (areas->NRows() == 0) { + return false; + } + + AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas)); + return true; +} + +// [ auto-flow && dense? ] <'grid-auto-columns'>? | +// <'grid-template-columns'> +bool +CSSParserImpl::ParseGridTemplateColumnsOrAutoFlow(bool aForGridShorthand) +{ + if (aForGridShorthand) { + auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_COLUMN); + if (res == CSSParseResult::Error) { + return false; + } + if (res == CSSParseResult::Ok) { + nsCSSValue value(eCSSUnit_None); + AppendValue(eCSSProperty_grid_template_columns, value); + return true; + } + } + return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns); +} + +bool +CSSParserImpl::ParseGridTemplate(bool aForGridShorthand) +{ + // none | + // subgrid | + // <'grid-template-rows'> / <'grid-template-columns'> | + // [ ? ? ? ]+ [ / ]? + // or additionally when aForGridShorthand is true: + // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_grid_template_areas, value); + AppendValue(eCSSProperty_grid_template_rows, value); + AppendValue(eCSSProperty_grid_template_columns, value); + return true; + } + + // 'none' can appear either by itself, + // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'> + if (ParseSingleTokenVariant(value, VARIANT_NONE, nullptr)) { + AppendValue(eCSSProperty_grid_template_rows, value); + AppendValue(eCSSProperty_grid_template_areas, value); + if (ExpectSymbol('/', true)) { + return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand); + } + AppendValue(eCSSProperty_grid_template_columns, value); + return true; + } + + // 'subgrid' can appear either by itself, + // or as the beginning of <'grid-template-rows'> / <'grid-template-columns'> + nsSubstring* ident = NextIdent(); + if (ident) { + if (ident->LowerCaseEqualsLiteral("subgrid")) { + if (!nsLayoutUtils::IsGridTemplateSubgridValueEnabled()) { + REPORT_UNEXPECTED(PESubgridNotSupported); + return false; + } + if (!ParseOptionalLineNameListAfterSubgrid(value)) { + return false; + } + AppendValue(eCSSProperty_grid_template_rows, value); + AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(eCSSUnit_None)); + if (ExpectSymbol('/', true)) { + return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand); + } + if (value.GetListValue()->mNext) { + // Non-empty after 'subgrid'. + // This is only valid as part of <'grid-template-rows'>, + // which must be followed by a slash. + return false; + } + // 'subgrid' by itself sets both grid-template-rows/columns. + AppendValue(eCSSProperty_grid_template_columns, value); + return true; + } + UngetToken(); + } + + // [ ? ] here is ambiguous: + // it can be either the start of a (in a <'grid-template-rows'>), + // or the start of [ ? ? ? ]+ + nsCSSValue firstLineNames; + if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error || + !GetToken(true)) { + return false; + } + if (mToken.mType == eCSSToken_String) { + // It's the [ ? ? ? ]+ case. + if (!ParseGridTemplateAfterString(firstLineNames)) { + return false; + } + // Parse an optional [ / ] as the columns value. + if (ExpectSymbol('/', true)) { + return ParseGridTrackList(eCSSProperty_grid_template_columns, + GridTrackListFlags::eExplicitTrackList); + } + value.SetNoneValue(); // absent means 'none' + AppendValue(eCSSProperty_grid_template_columns, value); + return true; + } + UngetToken(); + + // Finish parsing <'grid-template-rows'> with the |firstLineNames| we have, + // and then parse a mandatory [ / <'grid-template-columns'> ]. + if (!ParseGridTrackListWithFirstLineNames(value, firstLineNames) || + !ExpectSymbol('/', true)) { + return false; + } + AppendValue(eCSSProperty_grid_template_rows, value); + value.SetNoneValue(); + AppendValue(eCSSProperty_grid_template_areas, value); + return ParseGridTemplateColumnsOrAutoFlow(aForGridShorthand); +} + +// Helper for parsing the 'grid-template' shorthand: +// Parse [ ? ? ? ]+ +// with a ? already consumed, stored in |aFirstLineNames|, +// and the current token a +bool +CSSParserImpl::ParseGridTemplateAfterString(const nsCSSValue& aFirstLineNames) +{ + MOZ_ASSERT(mToken.mType == eCSSToken_String, + "ParseGridTemplateAfterString called with a non-string token"); + + nsCSSValue rowsValue; + RefPtr areas = + new css::GridTemplateAreasValue(); + nsDataHashtable areaIndices; + nsCSSValueList* rowsItem = rowsValue.SetListValue(); + rowsItem->mValue = aFirstLineNames; + + for (;;) { + if (!ParseGridTemplateAreasLine(mToken.mIdent, areas, areaIndices)) { + return false; + } + + rowsItem->mNext = new nsCSSValueList; + rowsItem = rowsItem->mNext; + CSSParseResult result = ParseGridTrackSize(rowsItem->mValue); + if (result == CSSParseResult::Error) { + return false; + } + if (result == CSSParseResult::NotFound) { + rowsItem->mValue.SetAutoValue(); + } + + rowsItem->mNext = new nsCSSValueList; + rowsItem = rowsItem->mNext; + result = ParseGridLineNames(rowsItem->mValue); + if (result == CSSParseResult::Error) { + return false; + } + if (result == CSSParseResult::Ok) { + // Append to the same list as the previous call to ParseGridLineNames. + result = ParseGridLineNames(rowsItem->mValue); + if (result == CSSParseResult::Error) { + return false; + } + if (result == CSSParseResult::Ok) { + // Parsed twice. + // The property value can not end here, we expect a string next. + if (!GetToken(true)) { + return false; + } + if (eCSSToken_String != mToken.mType) { + UngetToken(); + return false; + } + continue; + } + } + + // Did not find a . + // Next, we expect either a string or the end of the property value. + if (!GetToken(true)) { + break; + } + if (eCSSToken_String != mToken.mType) { + UngetToken(); + break; + } + } + + AppendValue(eCSSProperty_grid_template_areas, nsCSSValue(areas)); + AppendValue(eCSSProperty_grid_template_rows, rowsValue); + return true; +} + +// <'grid-template'> | +// <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? | +// [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'> +bool +CSSParserImpl::ParseGrid() +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + for (const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid); + *subprops != eCSSProperty_UNKNOWN; ++subprops) { + AppendValue(*subprops, value); + } + return true; + } + + // https://drafts.csswg.org/css-grid/#grid-shorthand + // "Also, the gutter properties are reset by this shorthand, + // even though they can't be set by it." + value.SetFloatValue(0.0f, eCSSUnit_Pixel); + AppendValue(eCSSProperty_grid_row_gap, value); + AppendValue(eCSSProperty_grid_column_gap, value); + + // [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'> + auto res = ParseGridShorthandAutoProps(NS_STYLE_GRID_AUTO_FLOW_ROW); + if (res == CSSParseResult::Error) { + return false; + } + if (res == CSSParseResult::Ok) { + value.SetAutoValue(); + AppendValue(eCSSProperty_grid_auto_columns, value); + nsCSSValue none(eCSSUnit_None); + AppendValue(eCSSProperty_grid_template_areas, none); + AppendValue(eCSSProperty_grid_template_rows, none); + if (!ExpectSymbol('/', true)) { + return false; + } + return ParseGridTemplateColumnsRows(eCSSProperty_grid_template_columns); + } + + // Set remaining subproperties that might not be set by ParseGridTemplate to + // their initial values and then parse <'grid-template'> | + // <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? . + value.SetIntValue(NS_STYLE_GRID_AUTO_FLOW_ROW, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_grid_auto_flow, value); + value.SetAutoValue(); + AppendValue(eCSSProperty_grid_auto_rows, value); + AppendValue(eCSSProperty_grid_auto_columns, value); + return ParseGridTemplate(true); +} + +// Parse [ auto-flow && dense? ] <'grid-auto-[rows|columns]'>? for the 'grid' +// shorthand. If aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW then we're +// parsing row values, otherwise column values. +CSSParseResult +CSSParserImpl::ParseGridShorthandAutoProps(int32_t aAutoFlowAxis) +{ + MOZ_ASSERT(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW || + aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_COLUMN); + if (!GetToken(true)) { + return CSSParseResult::NotFound; + } + // [ auto-flow && dense? ] + int32_t autoFlowValue = 0; + if (mToken.mType == eCSSToken_Ident) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (keyword == eCSSKeyword_auto_flow) { + autoFlowValue = aAutoFlowAxis; + if (GetToken(true)) { + if (mToken.mType == eCSSToken_Ident && + nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_dense) { + autoFlowValue |= NS_STYLE_GRID_AUTO_FLOW_DENSE; + } else { + UngetToken(); + } + } + } else if (keyword == eCSSKeyword_dense) { + if (!GetToken(true)) { + return CSSParseResult::Error; + } + if (mToken.mType != eCSSToken_Ident || + nsCSSKeywords::LookupKeyword(mToken.mIdent) != eCSSKeyword_auto_flow) { + UngetToken(); + return CSSParseResult::Error; + } + autoFlowValue = aAutoFlowAxis | NS_STYLE_GRID_AUTO_FLOW_DENSE; + } + } + if (autoFlowValue) { + nsCSSValue value; + value.SetIntValue(autoFlowValue, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_grid_auto_flow, value); + } else { + UngetToken(); + return CSSParseResult::NotFound; + } + + // <'grid-auto-[rows|columns]'>? + nsCSSValue autoTrackValue; + CSSParseResult result = ParseGridTrackSize(autoTrackValue); + if (result == CSSParseResult::Error) { + return result; + } + if (result == CSSParseResult::NotFound) { + autoTrackValue.SetAutoValue(); + } + AppendValue(aAutoFlowAxis == NS_STYLE_GRID_AUTO_FLOW_ROW ? + eCSSProperty_grid_auto_rows : eCSSProperty_grid_auto_columns, + autoTrackValue); + return CSSParseResult::Ok; +} + +// Parse a . +// If successful, set aValue to eCSSUnit_Auto, +// or a eCSSUnit_List containing, in that order: +// +// * An optional eCSSUnit_Enumerated marking a "span" keyword. +// * An optional eCSSUnit_Integer +// * An optional eCSSUnit_Ident +// +// At least one of eCSSUnit_Integer or eCSSUnit_Ident is present. +bool +CSSParserImpl::ParseGridLine(nsCSSValue& aValue) +{ + // = + // auto | + // | + // [ && ? ] | + // [ span && [ || ] ] + // + // Syntactically, this simplifies to: + // + // = + // auto | + // [ span? && [ || ] ] + + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) { + return true; + } + + bool hasSpan = false; + bool hasIdent = false; + Maybe integer; + nsCSSValue ident; + +#ifdef MOZ_VALGRIND + // Make the contained value be defined even though we really want a + // Nothing here. This works around an otherwise difficult to avoid + // Memcheck false positive when this is compiled by gcc-5.3 -O2. + // See bug 1301856. + integer.emplace(0); + integer.reset(); +#endif + + if (!GetToken(true)) { + return false; + } + if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("span")) { + hasSpan = true; + if (!GetToken(true)) { + return false; + } + } + + do { + if (!hasIdent && + mToken.mType == eCSSToken_Ident && + ParseCustomIdent(ident, mToken.mIdent, kGridLineKeywords)) { + hasIdent = true; + } else if (integer.isNothing() && + mToken.mType == eCSSToken_Number && + mToken.mIntegerValid && + mToken.mInteger != 0) { + integer.emplace(mToken.mInteger); + } else { + UngetToken(); + break; + } + } while (!(integer.isSome() && hasIdent) && GetToken(true)); + + // Require at least one of or + if (!(integer.isSome() || hasIdent)) { + return false; + } + + if (!hasSpan && GetToken(true)) { + if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("span")) { + hasSpan = true; + } else { + UngetToken(); + } + } + + nsCSSValueList* item = aValue.SetListValue(); + if (hasSpan) { + // Given "span", a negative is invalid. + if (integer.isSome() && integer.ref() < 0) { + return false; + } + // '1' here is a dummy value. + // The mere presence of eCSSUnit_Enumerated indicates a "span" keyword. + item->mValue.SetIntValue(1, eCSSUnit_Enumerated); + item->mNext = new nsCSSValueList; + item = item->mNext; + } + if (integer.isSome()) { + item->mValue.SetIntValue(integer.ref(), eCSSUnit_Integer); + if (hasIdent) { + item->mNext = new nsCSSValueList; + item = item->mNext; + } + } + if (hasIdent) { + item->mValue = ident; + } + return true; +} + +bool +CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) || + ParseGridLine(value)) { + AppendValue(aPropID, value); + return true; + } + return false; +} + +// If |aFallback| is a List containing a single Ident, set |aValue| to that. +// Otherwise, set |aValue| to Auto. +// Used with |aFallback| from ParseGridLine() +static void +HandleGridLineFallback(const nsCSSValue& aFallback, nsCSSValue& aValue) +{ + if (aFallback.GetUnit() == eCSSUnit_List && + aFallback.GetListValue()->mValue.GetUnit() == eCSSUnit_Ident && + !aFallback.GetListValue()->mNext) { + aValue = aFallback; + } else { + aValue.SetAutoValue(); + } +} + +bool +CSSParserImpl::ParseGridColumnRow(nsCSSPropertyID aStartPropID, + nsCSSPropertyID aEndPropID) +{ + nsCSSValue value; + nsCSSValue secondValue; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + AppendValue(aStartPropID, value); + AppendValue(aEndPropID, value); + return true; + } + + if (!ParseGridLine(value)) { + return false; + } + if (GetToken(true)) { + if (mToken.IsSymbol('/')) { + if (ParseGridLine(secondValue)) { + AppendValue(aStartPropID, value); + AppendValue(aEndPropID, secondValue); + return true; + } else { + return false; + } + } + UngetToken(); + } + + // A single is repeated to both properties, + // anything else sets the grid-{column,row}-end property to 'auto'. + HandleGridLineFallback(value, secondValue); + + AppendValue(aStartPropID, value); + AppendValue(aEndPropID, secondValue); + return true; +} + +bool +CSSParserImpl::ParseGridArea() +{ + nsCSSValue values[4]; + if (ParseSingleTokenVariant(values[0], VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_grid_row_start, values[0]); + AppendValue(eCSSProperty_grid_column_start, values[0]); + AppendValue(eCSSProperty_grid_row_end, values[0]); + AppendValue(eCSSProperty_grid_column_end, values[0]); + return true; + } + + int32_t i = 0; + for (;;) { + if (!ParseGridLine(values[i])) { + return false; + } + if (++i == 4 || !GetToken(true)) { + break; + } + if (!mToken.IsSymbol('/')) { + UngetToken(); + break; + } + } + + MOZ_ASSERT(i >= 1, "should have parsed at least one grid-line (or returned)"); + if (i < 2) { + HandleGridLineFallback(values[0], values[1]); + } + if (i < 3) { + HandleGridLineFallback(values[0], values[2]); + } + if (i < 4) { + HandleGridLineFallback(values[1], values[3]); + } + + AppendValue(eCSSProperty_grid_row_start, values[0]); + AppendValue(eCSSProperty_grid_column_start, values[1]); + AppendValue(eCSSProperty_grid_row_end, values[2]); + AppendValue(eCSSProperty_grid_column_end, values[3]); + return true; +} + +bool +CSSParserImpl::ParseGridGap() +{ + nsCSSValue first; + if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_grid_row_gap, first); + AppendValue(eCSSProperty_grid_column_gap, first); + return true; + } + if (ParseNonNegativeVariant(first, VARIANT_LPCALC, nullptr) != + CSSParseResult::Ok) { + return false; + } + nsCSSValue second; + auto result = ParseNonNegativeVariant(second, VARIANT_LPCALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } + AppendValue(eCSSProperty_grid_row_gap, first); + AppendValue(eCSSProperty_grid_column_gap, + result == CSSParseResult::NotFound ? first : second); + return true; +} + +// normal | [ ?] +bool +CSSParserImpl::ParseInitialLetter() +{ + nsCSSValue value; + // 'inherit', 'initial', 'unset', 'none', and 'normal' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { + nsCSSValue first, second; + if (!ParseOneOrLargerNumber(first)) { + return false; + } + + if (!ParseOneOrLargerInteger(second)) { + AppendValue(eCSSProperty_initial_letter, first); + return true; + } else { + RefPtr val = nsCSSValue::Array::Create(2); + val->Item(0) = first; + val->Item(1) = second; + value.SetArrayValue(val, eCSSUnit_Array); + } + } + AppendValue(eCSSProperty_initial_letter, value); + return true; +} + +// [ $aTable && ? ] ? +// $aTable is for or +bool +CSSParserImpl::ParseAlignJustifyPosition(nsCSSValue& aResult, + const KTableEntry aTable[]) +{ + nsCSSValue pos, overflowPos; + int32_t value = 0; + if (ParseEnum(pos, aTable)) { + value = pos.GetIntValue(); + if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) { + value |= overflowPos.GetIntValue(); + } + aResult.SetIntValue(value, eCSSUnit_Enumerated); + return true; + } + if (ParseEnum(overflowPos, nsCSSProps::kAlignOverflowPosition)) { + if (ParseEnum(pos, aTable)) { + aResult.SetIntValue(pos.GetIntValue() | overflowPos.GetIntValue(), + eCSSUnit_Enumerated); + return true; + } + return false; // must be followed by a value in $table + } + return true; +} + +// auto | normal | stretch | | +// [ && ? ] | +// [ legacy && [ left | right | center ] ] +bool +CSSParserImpl::ParseJustifyItems() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + if (MOZ_UNLIKELY(ParseEnum(value, nsCSSProps::kAlignLegacy))) { + nsCSSValue legacy; + if (!ParseEnum(legacy, nsCSSProps::kAlignLegacyPosition)) { + return false; // leading 'legacy' not followed by 'left' etc is an error + } + value.SetIntValue(value.GetIntValue() | legacy.GetIntValue(), + eCSSUnit_Enumerated); + } else { + if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) { + if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) || + value.GetUnit() == eCSSUnit_Null) { + return false; + } + // check for a trailing 'legacy' after 'left' etc + auto val = value.GetIntValue(); + if (val == NS_STYLE_JUSTIFY_CENTER || + val == NS_STYLE_JUSTIFY_LEFT || + val == NS_STYLE_JUSTIFY_RIGHT) { + nsCSSValue legacy; + if (ParseEnum(legacy, nsCSSProps::kAlignLegacy)) { + value.SetIntValue(val | legacy.GetIntValue(), eCSSUnit_Enumerated); + } + } + } + } + } + AppendValue(eCSSProperty_justify_items, value); + return true; +} + +// normal | stretch | | +// [ ? && ] +bool +CSSParserImpl::ParseAlignItems() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalStretchBaseline)) { + if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) || + value.GetUnit() == eCSSUnit_Null) { + return false; + } + } + } + AppendValue(eCSSProperty_align_items, value); + return true; +} + +// auto | normal | stretch | | +// [ ? && ] +bool +CSSParserImpl::ParseAlignJustifySelf(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseAlignEnum(value, nsCSSProps::kAlignAutoNormalStretchBaseline)) { + if (!ParseAlignJustifyPosition(value, nsCSSProps::kAlignSelfPosition) || + value.GetUnit() == eCSSUnit_Null) { + return false; + } + } + } + AppendValue(aPropID, value); + return true; +} + +// normal | | [ || +// [ ? && ] ] +// (the part after the || is called <*-position> below) +bool +CSSParserImpl::ParseAlignJustifyContent(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseAlignEnum(value, nsCSSProps::kAlignNormalBaseline)) { + nsCSSValue fallbackValue; + if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) { + if (!ParseAlignJustifyPosition(fallbackValue, + nsCSSProps::kAlignContentPosition) || + fallbackValue.GetUnit() == eCSSUnit_Null) { + return false; + } + // optional after <*-position> ... + if (!ParseEnum(value, nsCSSProps::kAlignContentDistribution)) { + // ... is missing so the <*-position> is the value, not the fallback + value = fallbackValue; + fallbackValue.Reset(); + } + } else { + // any optional <*-position> is a fallback value + if (!ParseAlignJustifyPosition(fallbackValue, + nsCSSProps::kAlignContentPosition)) { + return false; + } + } + if (fallbackValue.GetUnit() != eCSSUnit_Null) { + auto fallback = fallbackValue.GetIntValue(); + value.SetIntValue(value.GetIntValue() | + (fallback << NS_STYLE_ALIGN_ALL_SHIFT), + eCSSUnit_Enumerated); + } + } + } + AppendValue(aPropID, value); + return true; +} + +// place-content: [ normal | | | +// ]{1,2} +bool +CSSParserImpl::ParsePlaceContent() +{ + nsCSSValue first; + if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_align_content, first); + AppendValue(eCSSProperty_justify_content, first); + return true; + } + if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalBaseline) && + !ParseEnum(first, nsCSSProps::kAlignContentDistribution) && + !ParseEnum(first, nsCSSProps::kAlignContentPosition)) { + return false; + } + AppendValue(eCSSProperty_align_content, first); + nsCSSValue second; + if (!ParseAlignEnum(second, nsCSSProps::kAlignNormalBaseline) && + !ParseEnum(second, nsCSSProps::kAlignContentDistribution) && + !ParseEnum(second, nsCSSProps::kAlignContentPosition)) { + AppendValue(eCSSProperty_justify_content, first); + } else { + AppendValue(eCSSProperty_justify_content, second); + } + return true; +} + +// place-items: [ auto | ]? +// = [ normal | stretch | | ] +bool +CSSParserImpl::ParsePlaceItems() +{ + nsCSSValue first; + if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_align_items, first); + AppendValue(eCSSProperty_justify_items, first); + return true; + } + if (!ParseAlignEnum(first, nsCSSProps::kAlignNormalStretchBaseline) && + !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) { + return false; + } + AppendValue(eCSSProperty_align_items, first); + nsCSSValue second; + // Note: 'auto' is valid for justify-items, but not align-items. + if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) && + !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) { + AppendValue(eCSSProperty_justify_items, first); + } else { + AppendValue(eCSSProperty_justify_items, second); + } + return true; +} + +// place-self: [ auto | normal | stretch | | +// ]{1,2} +bool +CSSParserImpl::ParsePlaceSelf() +{ + nsCSSValue first; + if (ParseSingleTokenVariant(first, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_align_self, first); + AppendValue(eCSSProperty_justify_self, first); + return true; + } + if (!ParseAlignEnum(first, nsCSSProps::kAlignAutoNormalStretchBaseline) && + !ParseEnum(first, nsCSSProps::kAlignSelfPosition)) { + return false; + } + AppendValue(eCSSProperty_align_self, first); + nsCSSValue second; + if (!ParseAlignEnum(second, nsCSSProps::kAlignAutoNormalStretchBaseline) && + !ParseEnum(second, nsCSSProps::kAlignSelfPosition)) { + AppendValue(eCSSProperty_justify_self, first); + } else { + AppendValue(eCSSProperty_justify_self, second); + } + return true; +} + +// : [ | ]? +bool +CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) +{ + nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); + CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + stop->mIsInterpolationHint = true; + } + + // Stop positions do not have to fall between the starting-point and + // ending-point, so we don't use ParseNonNegativeVariant. + result = ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + if (stop->mIsInterpolationHint) { + return false; + } + stop->mLocation.SetNoneValue(); + } + return true; +} + +// Helper for ParseLinearGradient -- returns true iff aPosition represents a +// box-position value which was parsed with only edge keywords. +// e.g. "left top", or "bottom", but not "left 10px" +// +// (NOTE: Even though callers may want to exclude explicit "center", we still +// need to allow for _CENTER here, because omitted position-values (e.g. the +// x-component of a value like "top") will have been parsed as being *implicit* +// center. The correct way to disallow *explicit* center is to pass "false" for +// ParseBoxPositionValues()'s "aAllowExplicitCenter" parameter, before you +// call this function.) +static bool +IsBoxPositionStrictlyEdgeKeywords(nsCSSValuePair& aPosition) +{ + const nsCSSValue& xValue = aPosition.mXValue; + const nsCSSValue& yValue = aPosition.mYValue; + return (xValue.GetUnit() == eCSSUnit_Enumerated && + (xValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_LEFT | + NS_STYLE_IMAGELAYER_POSITION_CENTER | + NS_STYLE_IMAGELAYER_POSITION_RIGHT)) && + yValue.GetUnit() == eCSSUnit_Enumerated && + (yValue.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_TOP | + NS_STYLE_IMAGELAYER_POSITION_CENTER | + NS_STYLE_IMAGELAYER_POSITION_BOTTOM))); +} + +// +// : linear-gradient( ? ')' +// | radial-gradient( ? ')' +// +// : [ to [left | right] || [top | bottom] ] , +// | +// : [ || ] [ at ]? , +// | [ at ] , +// | ? ? +// : circle | ellipse +// : closest-side | closest-corner | farthest-side | farthest-corner +// | | [ | ]{2} +// +// : [ || ] , +// +// : [ || ] , +// : closest-side | closest-corner | farthest-side +// | farthest-corner | contain | cover +// +// : , [, ]* +bool +CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, + uint8_t aFlags) +{ + RefPtr cssGradient + = new nsCSSValueGradient(false, aFlags & eGradient_Repeating); + + if (!GetToken(true)) { + return false; + } + + // Check for "to" syntax (but not if parsing a -webkit-linear-gradient) + if (!(aFlags & eGradient_WebkitLegacy) && + mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("to")) { + + // "to" syntax doesn't allow explicit "center" + if (!ParseBoxPositionValues(cssGradient->mBgPos, false, false)) { + SkipUntil(')'); + return false; + } + + // [ to [left | right] || [top | bottom] ] , + if (!IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) { + SkipUntil(')'); + return false; + } + + if (!ExpectSymbol(',', true)) { + SkipUntil(')'); + return false; + } + + return ParseGradientColorStops(cssGradient, aValue); + } + + if (!(aFlags & eGradient_AnyLegacy)) { + // We're parsing an unprefixed linear-gradient, and we tried & failed to + // parse a 'to' token above. Put the token back & try to re-parse our + // expression as ? + UngetToken(); + + // , + if (ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) && + !ExpectSymbol(',', true)) { + SkipUntil(')'); + return false; + } + + return ParseGradientColorStops(cssGradient, aValue); + } + + // If we get here, we're parsing a prefixed linear-gradient expression. Put + // back the first token (which we may have checked for "to" above) and try to + // parse expression as ? + bool haveGradientLine = IsLegacyGradientLine(mToken.mType, mToken.mIdent); + UngetToken(); + + if (haveGradientLine) { + // Parse a + cssGradient->mIsLegacySyntax = true; + // In -webkit-linear-gradient expressions (handled below), we need to accept + // unitless 0 for angles, to match WebKit/Blink. + int32_t angleFlags = (aFlags & eGradient_WebkitLegacy) ? + VARIANT_ANGLE | VARIANT_ZERO_ANGLE : + VARIANT_ANGLE; + + bool haveAngle = + ParseSingleTokenVariant(cssGradient->mAngle, angleFlags, nullptr); + + // If we got an angle, we might now have a comma, ending the gradient-line. + bool haveAngleComma = haveAngle && ExpectSymbol(',', true); + + // If we're webkit-prefixed & didn't get an angle, + // OR if we're moz-prefixed & didn't get an angle+comma, + // then proceed to parse a box-position. + if (((aFlags & eGradient_WebkitLegacy) && !haveAngle) || + ((aFlags & eGradient_MozLegacy) && !haveAngleComma)) { + // (Note: 3rd arg controls whether the "center" keyword is allowed. + // -moz-linear-gradient allows it; -webkit-linear-gradient does not.) + if (!ParseBoxPositionValues(cssGradient->mBgPos, false, + (aFlags & eGradient_MozLegacy))) { + SkipUntil(')'); + return false; + } + + // -webkit-linear-gradient only supports edge keywords here. + if ((aFlags & eGradient_WebkitLegacy) && + !IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) { + SkipUntil(')'); + return false; + } + + if (!ExpectSymbol(',', true) && + // If we didn't already get an angle, and we're not -webkit prefixed, + // we can parse an angle+comma now. Otherwise it's an error. + (haveAngle || + (aFlags & eGradient_WebkitLegacy) || + !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, + nullptr) || + // now we better have a comma + !ExpectSymbol(',', true))) { + SkipUntil(')'); + return false; + } + } + } + + return ParseGradientColorStops(cssGradient, aValue); +} + +bool +CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, + uint8_t aFlags) +{ + RefPtr cssGradient + = new nsCSSValueGradient(true, aFlags & eGradient_Repeating); + + // [ || ] + bool haveShape = + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); + + bool haveSize = + ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, + (aFlags & eGradient_AnyLegacy) ? + nsCSSProps::kRadialGradientLegacySizeKTable : + nsCSSProps::kRadialGradientSizeKTable); + if (haveSize) { + if (!haveShape) { + // + haveShape = + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); + } + } else if (!(aFlags & eGradient_AnyLegacy)) { + // Save RadialShape before parsing RadiusX because RadialShape and + // RadiusX share the storage. + int32_t shape = + cssGradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated ? + cssGradient->GetRadialShape().GetIntValue() : -1; + // | [ | ]{2} + cssGradient->mIsExplicitSize = true; + haveSize = + ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, + nullptr); + if (!haveSize) { + // It was not an explicit size after all. + // Note that ParseNonNegativeVariant may have put something + // invalid into our storage, but only in the case where it was + // rejected only for being negative. Since this means the token + // was a length or a percentage, we know it's not valid syntax + // (which must be a comma, the 'at' keyword, or a color), so we + // know this value will be dropped. This means it doesn't matter + // that we have something invalid in our storage. + cssGradient->mIsExplicitSize = false; + } else { + // vertical extent is optional + bool haveYSize = + ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusY(), + VARIANT_LP, nullptr); + if (!haveShape) { + nsCSSValue shapeValue; + haveShape = + ParseSingleTokenVariant(shapeValue, VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); + if (haveShape) { + shape = shapeValue.GetIntValue(); + } + } + if (haveYSize + ? shape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR + : cssGradient->GetRadiusX().GetUnit() == eCSSUnit_Percent || + shape == NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL) { + SkipUntil(')'); + return false; + } + } + } + + if ((haveShape || haveSize) && ExpectSymbol(',', true)) { + // [ || ] , + return ParseGradientColorStops(cssGradient, aValue); + } + + if (!GetToken(true)) { + return false; + } + + if (!(aFlags & eGradient_AnyLegacy)) { + if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("at")) { + // [ || ]? at , + if (!ParseBoxPositionValues(cssGradient->mBgPos, false) || + !ExpectSymbol(',', true)) { + SkipUntil(')'); + return false; + } + + return ParseGradientColorStops(cssGradient, aValue); + } + + // only + UngetToken(); + return ParseGradientColorStops(cssGradient, aValue); + } + MOZ_ASSERT(!cssGradient->mIsExplicitSize); + + nsCSSTokenType ty = mToken.mType; + nsString id = mToken.mIdent; + UngetToken(); + + // + bool haveGradientLine = false; + // if we already encountered a shape or size, + // we can not have a gradient-line in legacy syntax + if (!haveShape && !haveSize) { + haveGradientLine = IsLegacyGradientLine(ty, id); + } + if (haveGradientLine) { + // Note: -webkit-radial-gradient() doesn't accept angles. + bool haveAngle = (aFlags & eGradient_WebkitLegacy) + ? false + : ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); + + // If we got an angle, we might now have a comma, ending the gradient-line + if (!haveAngle || !ExpectSymbol(',', true)) { + if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) { + SkipUntil(')'); + return false; + } + + if (!ExpectSymbol(',', true) && + // If we didn't already get an angle, and we're not -webkit prefixed, + // can parse an angle+comma now. Otherwise it's an error. + (haveAngle || + (aFlags & eGradient_WebkitLegacy) || + !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, + nullptr) || + // now we better have a comma + !ExpectSymbol(',', true))) { + SkipUntil(')'); + return false; + } + } + + if (cssGradient->mAngle.GetUnit() != eCSSUnit_None) { + cssGradient->mIsLegacySyntax = true; + } + } + + // radial gradients might have a shape and size here for legacy syntax + if (!haveShape && !haveSize) { + haveShape = + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); + haveSize = + ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientLegacySizeKTable); + + // could be in either order + if (!haveShape) { + haveShape = + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); + } + } + + if ((haveShape || haveSize) && !ExpectSymbol(',', true)) { + SkipUntil(')'); + return false; + } + + return ParseGradientColorStops(cssGradient, aValue); +} + +bool +CSSParserImpl::IsLegacyGradientLine(const nsCSSTokenType& aType, + const nsString& aId) +{ + // N.B. ParseBoxPositionValues is not guaranteed to put back + // everything it scanned if it fails, so we must only call it + // if there is no alternative to consuming a . + // ParseVariant, as used here, will either succeed and consume + // a single token, or fail and consume none, so we can be more + // cavalier about calling it. + + bool haveGradientLine = false; + switch (aType) { + case eCSSToken_Percentage: + case eCSSToken_Number: + case eCSSToken_Dimension: + haveGradientLine = true; + break; + + case eCSSToken_Function: + if (aId.LowerCaseEqualsLiteral("calc") || + aId.LowerCaseEqualsLiteral("-moz-calc")) { + haveGradientLine = true; + break; + } + MOZ_FALLTHROUGH; + case eCSSToken_ID: + case eCSSToken_Hash: + // this is a color + break; + + case eCSSToken_Ident: { + // This is only a gradient line if it's a box position keyword. + nsCSSKeyword kw = nsCSSKeywords::LookupKeyword(aId); + int32_t junk; + if (kw != eCSSKeyword_UNKNOWN && + nsCSSProps::FindKeyword(kw, nsCSSProps::kImageLayerPositionKTable, + junk)) { + haveGradientLine = true; + } + break; + } + + default: + // error + break; + } + + return haveGradientLine; +} + +bool +CSSParserImpl::ParseGradientColorStops(nsCSSValueGradient* aGradient, + nsCSSValue& aValue) +{ + // At least two color stops are required + if (!ParseColorStop(aGradient) || + !ExpectSymbol(',', true) || + !ParseColorStop(aGradient)) { + SkipUntil(')'); + return false; + } + + // Additional color stops + while (ExpectSymbol(',', true)) { + if (!ParseColorStop(aGradient)) { + SkipUntil(')'); + return false; + } + } + + if (!ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + + // Check if interpolation hints are in the correct location + bool previousPointWasInterpolationHint = true; + for (size_t x = 0; x < aGradient->mStops.Length(); x++) { + bool isInterpolationHint = aGradient->mStops[x].mIsInterpolationHint; + if (isInterpolationHint && previousPointWasInterpolationHint) { + return false; + } + previousPointWasInterpolationHint = isInterpolationHint; + } + + if (previousPointWasInterpolationHint) { + return false; + } + + aValue.SetGradientValue(aGradient); + return true; +} + +// Parses the x or y component of a -webkit-gradient() expression. +// See ParseWebkitGradientPoint() documentation for more. +bool +CSSParserImpl::ParseWebkitGradientPointComponent(nsCSSValue& aComponent, + bool aIsHorizontal) +{ + if (!GetToken(true)) { + return false; + } + + // Keyword tables to use for keyword-matching + // (Keyword order is important; we assume the index can be multiplied by 50% + // to convert to a percent-valued component.) + static const nsCSSKeyword kHorizKeywords[] = { + eCSSKeyword_left, // 0% + eCSSKeyword_center, // 50% + eCSSKeyword_right // 100% + }; + static const nsCSSKeyword kVertKeywords[] = { + eCSSKeyword_top, // 0% + eCSSKeyword_center, // 50% + eCSSKeyword_bottom // 100% + }; + static const size_t kNumKeywords = MOZ_ARRAY_LENGTH(kHorizKeywords); + static_assert(kNumKeywords == MOZ_ARRAY_LENGTH(kVertKeywords), + "Horizontal & vertical keyword tables must have same count"); + + // Try to parse the component as a number, or a percent, or a + // keyword-converted-to-percent. + if (mToken.mType == eCSSToken_Number) { + aComponent.SetFloatValue(mToken.mNumber, eCSSUnit_Pixel); + } else if (mToken.mType == eCSSToken_Percentage) { + aComponent.SetPercentValue(mToken.mNumber); + } else if (mToken.mType == eCSSToken_Ident) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (keyword == eCSSKeyword_UNKNOWN) { + return false; + } + // Choose our keyword table: + const nsCSSKeyword* kwTable = aIsHorizontal ? kHorizKeywords : kVertKeywords; + // Convert keyword to percent value (0%, 50%, or 100%) + bool didAcceptKeyword = false; + for (size_t i = 0; i < kNumKeywords; i++) { + if (keyword == kwTable[i]) { + // 0%, 50%, or 100%: + aComponent.SetPercentValue(i * 0.5); + didAcceptKeyword = true; + break; + } + } + if (!didAcceptKeyword) { + return false; + } + } else { + // Unrecognized token type. Put it back. (It might be a closing-paren of an + // invalid -webkit-gradient(...) expression, and we need to be sure caller + // can see it & stops parsing at that point.) + UngetToken(); + return false; + } + + MOZ_ASSERT(aComponent.GetUnit() == eCSSUnit_Pixel || + aComponent.GetUnit() == eCSSUnit_Percent, + "If we get here, we should've successfully parsed a number (as a " + "pixel length), a percent, or a keyword (converted to percent)"); + return true; +} + +// This function parses a "" expression for -webkit-gradient(...) +// Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ : +// "A point is a pair of space-separated values. +// The syntax supports numbers, percentages or +// the keywords top, bottom, left and right +// for point values." +// +// Two additional notes: +// - WebKit also accepts the "center" keyword (not listed in the text above). +// - WebKit only accepts horizontal-flavored keywords (left/center/right) in +// the first ("x") component, and vertical-flavored keywords +// (top/center/bottom) in the second ("y") component. (This is different +// from the standard gradient syntax, which accepts both orderings, e.g. +// "top left" as well as "left top".) +bool +CSSParserImpl::ParseWebkitGradientPoint(nsCSSValuePair& aPoint) +{ + return ParseWebkitGradientPointComponent(aPoint.mXValue, true) && + ParseWebkitGradientPointComponent(aPoint.mYValue, false); +} + +// Parse the next token as a (for a in a -webkit-gradient +// expresison). Returns true on success; returns false & puts back +// whatever it parsed on failure. +bool +CSSParserImpl::ParseWebkitGradientRadius(float& aRadius) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_Number) { + UngetToken(); + return false; + } + + aRadius = mToken.mNumber; + return true; +} + +// Parse one of: +// color-stop(number|percent, color) +// from(color) +// to(color) +// +// Quoting https://www.webkit.org/blog/175/introducing-css-gradients/ : +// A stop is a function, color-stop, that takes two arguments, the stop value +// (either a percentage or a number between 0 and 1.0), and a color (any +// valid CSS color). In addition the shorthand functions from and to are +// supported. These functions only require a color argument and are +// equivalent to color-stop(0, ...) and color-stop(1.0, …) respectively. +bool +CSSParserImpl::ParseWebkitGradientColorStop(nsCSSValueGradient* aGradient) +{ + MOZ_ASSERT(aGradient, "null gradient"); + + if (!GetToken(true)) { + return false; + } + + // We're expecting color-stop(...), from(...), or to(...) which are all + // functions. Bail if we got anything else. + if (mToken.mType != eCSSToken_Function) { + UngetToken(); + return false; + } + + nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); + + // Parse color-stop location (or infer it, for shorthands "from"/"to"): + if (mToken.mIdent.LowerCaseEqualsLiteral("color-stop")) { + // Parse stop location, followed by comma. + if (!ParseSingleTokenVariant(stop->mLocation, + VARIANT_NUMBER | VARIANT_PERCENT, + nullptr) || + !ExpectSymbol(',', true)) { + SkipUntil(')'); // Skip to end of color-stop(...) expression. + return false; + } + + // If we got a , convert it to percentage for consistency: + if (stop->mLocation.GetUnit() == eCSSUnit_Number) { + stop->mLocation.SetPercentValue(stop->mLocation.GetFloatValue()); + } + } else if (mToken.mIdent.LowerCaseEqualsLiteral("from")) { + // Shorthand for color-stop(0%, ...) + stop->mLocation.SetPercentValue(0.0f); + } else if (mToken.mIdent.LowerCaseEqualsLiteral("to")) { + // Shorthand for color-stop(100%, ...) + stop->mLocation.SetPercentValue(1.0f); + } else { + // Unrecognized function name (invalid for a -webkit-gradient color stop). + UngetToken(); + return false; + } + + CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr); + if (result != CSSParseResult::Ok || + (stop->mColor.GetUnit() == eCSSUnit_EnumColor && + stop->mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR)) { + // Parse failure, or parsed "currentColor" which is forbidden in + // -webkit-gradient for some reason. + SkipUntil(')'); + return false; + } + + // Parse color-stop function close-paren + if (!ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + + MOZ_ASSERT(stop->mLocation.GetUnit() == eCSSUnit_Percent, + "Should produce only percent-valued stop-locations. " + "(Caller depends on this when sorting color stops.)"); + + return true; +} + +// Comparatison function to use for sorting -webkit-gradient() stops by +// location. This function assumes stops have percent-valued locations (and +// CSSParserImpl::ParseWebkitGradientColorStop should enforce this). +static bool +IsColorStopPctLocationLessThan(const nsCSSValueGradientStop& aStop1, + const nsCSSValueGradientStop& aStop2) { + return (aStop1.mLocation.GetPercentValue() < + aStop2.mLocation.GetPercentValue()); +} + +// This function parses a list of comma-separated color-stops for a +// -webkit-gradient(...) expression, and then pads & sorts the list as-needed. +bool +CSSParserImpl::ParseWebkitGradientColorStops(nsCSSValueGradient* aGradient) +{ + MOZ_ASSERT(aGradient, "null gradient"); + + // Parse any number of ", " expressions. (0 or more) + // Note: This is different from unprefixed gradient syntax, which + // requires at least 2 stops. + while (ExpectSymbol(',', true)) { + if (!ParseWebkitGradientColorStop(aGradient)) { + return false; + } + } + + // Pad up to 2 stops as-needed: + // (Modern gradient expressions are required to have at least 2 stops, so we + // depend on this internally -- e.g. we have an assertion about this in + // nsCSSRendering.cpp. -webkit-gradient syntax allows 0 stops or 1 stop, + // though, so we just pad up to 2 stops in this case). + + // If we have no stops, pad with transparent-black: + if (aGradient->mStops.IsEmpty()) { + nsCSSValueGradientStop* stop1 = aGradient->mStops.AppendElement(); + stop1->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0), + eCSSUnit_RGBAColor); + stop1->mLocation.SetPercentValue(0.0f); + + nsCSSValueGradientStop* stop2 = aGradient->mStops.AppendElement(); + stop2->mColor.SetIntegerColorValue(NS_RGBA(0, 0, 0, 0), + eCSSUnit_RGBAColor); + stop2->mLocation.SetPercentValue(1.0f); + } else if (aGradient->mStops.Length() == 1) { + // Copy whatever the author provided in the first stop: + nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); + *stop = aGradient->mStops[0]; + } else { + // We have >2 stops. Sort them in order of increasing location. + std::stable_sort(aGradient->mStops.begin(), + aGradient->mStops.end(), + IsColorStopPctLocationLessThan); + } + return true; +} + +// Compares aStartCoord to aEndCoord, and returns true iff they share the same +// unit (both pixel, or both percent) and aStartCoord is larger. +static bool +IsWebkitGradientCoordLarger(const nsCSSValue& aStartCoord, + const nsCSSValue& aEndCoord) +{ + if (aStartCoord.GetUnit() == eCSSUnit_Percent && + aEndCoord.GetUnit() == eCSSUnit_Percent) { + return aStartCoord.GetPercentValue() > aEndCoord.GetPercentValue(); + } + + if (aStartCoord.GetUnit() == eCSSUnit_Pixel && + aEndCoord.GetUnit() == eCSSUnit_Pixel) { + return aStartCoord.GetFloatValue() > aEndCoord.GetFloatValue(); + } + + // We can't compare them, since their units differ. Returning false suggests + // that aEndCoord is larger, which is probably a decent guess anyway. + return false; +} + +// Finalize our internal representation of a -webkit-gradient(linear, ...) +// expression, given the parsed points. (The parsed color stops +// should already be hanging off of the passed-in nsCSSValueGradient.) +// +// Note: linear gradients progress along a line between two points. The +// -webkit-gradient(linear, ...) syntax lets the author precisely specify the +// starting and ending point. However, our internal gradient structures +// only store one point, and the other point is implicitly its reflection +// across the painted area's center. (The legacy -moz-linear-gradient syntax +// also lets us store an angle.) +// +// In this function, we try to go from the two-point representation to an +// equivalent or approximately-equivalent one-point representation. +void +CSSParserImpl::FinalizeLinearWebkitGradient(nsCSSValueGradient* aGradient, + const nsCSSValuePair& aStartPoint, + const nsCSSValuePair& aEndPoint) +{ + MOZ_ASSERT(!aGradient->mIsRadial, "passed-in gradient must be linear"); + + // If the start & end points have the same Y-coordinate, then we can treat + // this as a horizontal gradient progressing towards the center of the left + // or right side. + if (aStartPoint.mYValue == aEndPoint.mYValue) { + aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, + eCSSUnit_Enumerated); + if (IsWebkitGradientCoordLarger(aStartPoint.mXValue, aEndPoint.mXValue)) { + aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_LEFT, + eCSSUnit_Enumerated); + } else { + aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_RIGHT, + eCSSUnit_Enumerated); + } + return; + } + + // If the start & end points have the same X-coordinate, then we can treat + // this as a horizontal gradient progressing towards the center of the top + // or bottom side. + if (aStartPoint.mXValue == aEndPoint.mXValue) { + aGradient->mBgPos.mXValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, + eCSSUnit_Enumerated); + if (IsWebkitGradientCoordLarger(aStartPoint.mYValue, aEndPoint.mYValue)) { + aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_TOP, + eCSSUnit_Enumerated); + } else { + aGradient->mBgPos.mYValue.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_BOTTOM, + eCSSUnit_Enumerated); + } + return; + } + + // OK, the gradient is angled, which means we likely can't represent it + // exactly in |aGradient|, without doing analysis on the two points to + // extract an angle (which we might not be able to do depending on the units + // used). For now, we'll just do something really basic -- just use the + // first point as if it were the starting point in a legacy + // -moz-linear-gradient() expression. That way, the rendered gradient will + // progress from this first point, towards the center of the covered element, + // to a reflected end point on the far side. Note that we have to use + // mIsLegacySyntax=true for this to work, because standardized (non-legacy) + // gradients place some restrictions on the reference point [namely, that it + // use percent units & be on the border of the element]. + aGradient->mIsLegacySyntax = true; + aGradient->mBgPos = aStartPoint; +} + +// Finalize our internal representation of a -webkit-gradient(radial, ...) +// expression, given the parsed points & radii. (The parsed color-stops +// should already be hanging off of the passed-in nsCSSValueGradient). +void +CSSParserImpl::FinalizeRadialWebkitGradient(nsCSSValueGradient* aGradient, + const nsCSSValuePair& aFirstCenter, + const nsCSSValuePair& aSecondCenter, + const float aFirstRadius, + const float aSecondRadius) +{ + MOZ_ASSERT(aGradient->mIsRadial, "passed-in gradient must be radial"); + + // NOTE: -webkit-gradient(radial, ...) has *two arbitrary circles*, with the + // gradient stretching between the circles' edges. In contrast, the standard + // syntax (and hence our data structures) can only represent *one* circle, + // with the gradient going from its center to its edge. To bridge this gap + // in expressiveness, we'll just see which of our two circles is smaller, and + // we'll treat that circle as if it were zero-sized and located at the center + // of the larger circle. Then, we'll be able to use the same data structures + // that we use for the standard radial-gradient syntax. + if (aSecondRadius >= aFirstRadius) { + // Second circle is larger. + aGradient->mBgPos = aSecondCenter; + aGradient->mIsExplicitSize = true; + aGradient->GetRadiusX().SetFloatValue(aSecondRadius, eCSSUnit_Pixel); + return; + } + + // First circle is larger, so we'll have it be the outer circle. + aGradient->mBgPos = aFirstCenter; + aGradient->mIsExplicitSize = true; + aGradient->GetRadiusX().SetFloatValue(aFirstRadius, eCSSUnit_Pixel); + + // For this to work properly (with the earlier color stops attached to the + // first circle), we need to also reverse the color-stop list, so that + // e.g. the author's "from" color is attached to the outer edge (the first + // circle), rather than attached to the center (the collapsed second circle). + std::reverse(aGradient->mStops.begin(), aGradient->mStops.end()); + + // And now invert the stop locations: + for (nsCSSValueGradientStop& colorStop : aGradient->mStops) { + float origLocation = colorStop.mLocation.GetPercentValue(); + colorStop.mLocation.SetPercentValue(1.0f - origLocation); + } +} + +bool +CSSParserImpl::ParseWebkitGradient(nsCSSValue& aValue) +{ + // Parse type of gradient + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_Ident) { + UngetToken(); // Important; the token might be ")", which we're about to + // seek to. + SkipUntil(')'); + return false; + } + + bool isRadial; + if (mToken.mIdent.LowerCaseEqualsLiteral("radial")) { + isRadial = true; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("linear")) { + isRadial = false; + } else { + // Unrecognized gradient type. + SkipUntil(')'); + return false; + } + + // Parse a comma + first point: + nsCSSValuePair firstPoint; + if (!ExpectSymbol(',', true) || + !ParseWebkitGradientPoint(firstPoint)) { + SkipUntil(')'); + return false; + } + + // If radial, parse comma + first radius: + float firstRadius; + if (isRadial) { + if (!ExpectSymbol(',', true) || + !ParseWebkitGradientRadius(firstRadius)) { + SkipUntil(')'); + return false; + } + } + + // Parse a comma + second point: + nsCSSValuePair secondPoint; + if (!ExpectSymbol(',', true) || + !ParseWebkitGradientPoint(secondPoint)) { + SkipUntil(')'); + return false; + } + + // If radial, parse comma + second radius: + float secondRadius; + if (isRadial) { + if (!ExpectSymbol(',', true) || + !ParseWebkitGradientRadius(secondRadius)) { + SkipUntil(')'); + return false; + } + } + + // Construct a nsCSSValueGradient object, and parse color stops into it: + RefPtr cssGradient = + new nsCSSValueGradient(isRadial, false /* aIsRepeating */); + + if (!ParseWebkitGradientColorStops(cssGradient) || + !ExpectSymbol(')', true)) { + // Failed to parse color-stops, or found trailing junk between them & ')'. + SkipUntil(')'); + return false; + } + + // Finish building cssGradient, based on our parsed positioning/sizing info: + if (isRadial) { + FinalizeRadialWebkitGradient(cssGradient, firstPoint, secondPoint, + firstRadius, secondRadius); + } else { + FinalizeLinearWebkitGradient(cssGradient, firstPoint, secondPoint); + } + + aValue.SetGradientValue(cssGradient); + return true; +} + +bool +CSSParserImpl::ParseWebkitTextStroke() +{ + static const nsCSSPropertyID kWebkitTextStrokeIDs[] = { + eCSSProperty__webkit_text_stroke_width, + eCSSProperty__webkit_text_stroke_color + }; + + const size_t numProps = MOZ_ARRAY_LENGTH(kWebkitTextStrokeIDs); + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, kWebkitTextStrokeIDs, numProps); + if (found < 1) { + return false; + } + + if (!(found & 1)) { // Provide default -webkit-text-stroke-width + values[0].SetFloatValue(0, eCSSUnit_Pixel); + } + + if (!(found & 2)) { // Provide default -webkit-text-stroke-color + values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + + for (size_t index = 0; index < numProps; ++index) { + AppendValue(kWebkitTextStrokeIDs[index], values[index]); + } + + return true; +} + + int32_t +CSSParserImpl::ParseChoice(nsCSSValue aValues[], + const nsCSSPropertyID aPropIDs[], int32_t aNumIDs) +{ + int32_t found = 0; + nsAutoParseCompoundProperty compound(this); + + int32_t loop; + for (loop = 0; loop < aNumIDs; loop++) { + // Try each property parser in order + int32_t hadFound = found; + int32_t index; + for (index = 0; index < aNumIDs; index++) { + int32_t bit = 1 << index; + if ((found & bit) == 0) { + CSSParseResult result = + ParseSingleValueProperty(aValues[index], aPropIDs[index]); + if (result == CSSParseResult::Error) { + return -1; + } + if (result == CSSParseResult::Ok) { + found |= bit; + // It's more efficient to break since it will reset |hadFound| + // to |found|. Furthermore, ParseListStyle depends on our going + // through the properties in order for each value.. + break; + } + } + } + if (found == hadFound) { // found nothing new + break; + } + } + if (0 < found) { + if (1 == found) { // only first property + if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit + for (loop = 1; loop < aNumIDs; loop++) { + aValues[loop].SetInheritValue(); + } + found = ((1 << aNumIDs) - 1); + } + else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial + for (loop = 1; loop < aNumIDs; loop++) { + aValues[loop].SetInitialValue(); + } + found = ((1 << aNumIDs) - 1); + } + else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset + for (loop = 1; loop < aNumIDs; loop++) { + aValues[loop].SetUnsetValue(); + } + found = ((1 << aNumIDs) - 1); + } + } + else { // more than one value, verify no inherits, initials or unsets + for (loop = 0; loop < aNumIDs; loop++) { + if (eCSSUnit_Inherit == aValues[loop].GetUnit()) { + found = -1; + break; + } + else if (eCSSUnit_Initial == aValues[loop].GetUnit()) { + found = -1; + break; + } + else if (eCSSUnit_Unset == aValues[loop].GetUnit()) { + found = -1; + break; + } + } + } + } + return found; +} + +void +CSSParserImpl::AppendValue(nsCSSPropertyID aPropID, const nsCSSValue& aValue) +{ + mTempData.AddLonghandProperty(aPropID, aValue); +} + +/** + * Parse a "box" property. Box properties have 1 to 4 values. When less + * than 4 values are provided a standard mapping is used to replicate + * existing values. + */ +bool +CSSParserImpl::ParseBoxProperties(const nsCSSPropertyID aPropIDs[]) +{ + // Get up to four values for the property + int32_t count = 0; + nsCSSRect result; + NS_FOR_CSS_SIDES (index) { + CSSParseResult parseResult = + ParseBoxProperty(result.*(nsCSSRect::sides[index]), aPropIDs[index]); + if (parseResult == CSSParseResult::NotFound) { + break; + } + if (parseResult == CSSParseResult::Error) { + return false; + } + count++; + } + if (count == 0) { + return false; + } + + if (1 < count) { // verify no more than single inherit, initial or unset + NS_FOR_CSS_SIDES (index) { + nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit(); + if (eCSSUnit_Inherit == unit || + eCSSUnit_Initial == unit || + eCSSUnit_Unset == unit) { + return false; + } + } + } + + // Provide missing values by replicating some of the values found + switch (count) { + case 1: // Make right == top + result.mRight = result.mTop; + MOZ_FALLTHROUGH; + case 2: // Make bottom == top + result.mBottom = result.mTop; + MOZ_FALLTHROUGH; + case 3: // Make left == right + result.mLeft = result.mRight; + } + + NS_FOR_CSS_SIDES (index) { + AppendValue(aPropIDs[index], result.*(nsCSSRect::sides[index])); + } + return true; +} + +// Similar to ParseBoxProperties, except there is only one property +// with the result as its value, not four. +bool +CSSParserImpl::ParseGroupedBoxProperty(int32_t aVariantMask, + /** outparam */ nsCSSValue& aValue, + uint32_t aRestrictions) +{ + nsCSSRect& result = aValue.SetRectValue(); + + int32_t count = 0; + NS_FOR_CSS_SIDES (index) { + CSSParseResult parseResult = + ParseVariantWithRestrictions(result.*(nsCSSRect::sides[index]), + aVariantMask, nullptr, + aRestrictions); + if (parseResult == CSSParseResult::NotFound) { + break; + } + if (parseResult == CSSParseResult::Error) { + return false; + } + count++; + } + + if (count == 0) { + return false; + } + + // Provide missing values by replicating some of the values found + switch (count) { + case 1: // Make right == top + result.mRight = result.mTop; + MOZ_FALLTHROUGH; + case 2: // Make bottom == top + result.mBottom = result.mTop; + MOZ_FALLTHROUGH; + case 3: // Make left == right + result.mLeft = result.mRight; + } + + return true; +} + +bool +CSSParserImpl::ParseBoxCornerRadius(nsCSSPropertyID aPropID) +{ + nsCSSValue dimenX, dimenY; + // required first value + if (ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr) != + CSSParseResult::Ok) { + return false; + } + + // optional second value (forbidden if first value is inherit/initial/unset) + if (dimenX.GetUnit() != eCSSUnit_Inherit && + dimenX.GetUnit() != eCSSUnit_Initial && + dimenX.GetUnit() != eCSSUnit_Unset) { + if (ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr) == + CSSParseResult::Error) { + return false; + } + } + + if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) { + AppendValue(aPropID, dimenX); + } else { + nsCSSValue value; + value.SetPairValue(dimenX, dimenY); + AppendValue(aPropID, value); + } + return true; +} + +bool +CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[]) +{ + // Rectangles are used as scratch storage. + // top => top-left, right => top-right, + // bottom => bottom-right, left => bottom-left. + nsCSSRect dimenX, dimenY; + int32_t countX = 0, countY = 0; + + NS_FOR_CSS_SIDES (side) { + CSSParseResult result = + ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side], + (side > 0 ? 0 : VARIANT_INHERIT) | + VARIANT_LP | VARIANT_CALC, + nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + break; + } + countX++; + } + if (countX == 0) + return false; + + if (ExpectSymbol('/', true)) { + NS_FOR_CSS_SIDES (side) { + CSSParseResult result = + ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side], + VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + break; + } + countY++; + } + if (countY == 0) + return false; + } + + // if 'initial', 'inherit' or 'unset' was used, it must be the only value + if (countX > 1 || countY > 0) { + nsCSSUnit unit = dimenX.mTop.GetUnit(); + if (eCSSUnit_Inherit == unit || + eCSSUnit_Initial == unit || + eCSSUnit_Unset == unit) + return false; + } + + // if we have no Y-values, use the X-values + if (countY == 0) { + dimenY = dimenX; + countY = countX; + } + + // Provide missing values by replicating some of the values found + switch (countX) { + case 1: // Make top-right same as top-left + dimenX.mRight = dimenX.mTop; + MOZ_FALLTHROUGH; + case 2: // Make bottom-right same as top-left + dimenX.mBottom = dimenX.mTop; + MOZ_FALLTHROUGH; + case 3: // Make bottom-left same as top-right + dimenX.mLeft = dimenX.mRight; + } + + switch (countY) { + case 1: // Make top-right same as top-left + dimenY.mRight = dimenY.mTop; + MOZ_FALLTHROUGH; + case 2: // Make bottom-right same as top-left + dimenY.mBottom = dimenY.mTop; + MOZ_FALLTHROUGH; + case 3: // Make bottom-left same as top-right + dimenY.mLeft = dimenY.mRight; + } + + NS_FOR_CSS_SIDES(side) { + nsCSSValue& x = dimenX.*nsCSSRect::sides[side]; + nsCSSValue& y = dimenY.*nsCSSRect::sides[side]; + + if (x == y) { + array[side] = x; + } else { + nsCSSValue pair; + pair.SetPairValue(x, y); + array[side] = pair; + } + } + return true; +} + +bool +CSSParserImpl::ParseBoxCornerRadii(const nsCSSPropertyID aPropIDs[]) +{ + nsCSSValue value[4]; + if (!ParseBoxCornerRadiiInternals(value)) { + return false; + } + + NS_FOR_CSS_SIDES(side) { + AppendValue(aPropIDs[side], value[side]); + } + return true; +} + +// These must be in CSS order (top,right,bottom,left) for indexing to work +static const nsCSSPropertyID kBorderStyleIDs[] = { + eCSSProperty_border_top_style, + eCSSProperty_border_right_style, + eCSSProperty_border_bottom_style, + eCSSProperty_border_left_style +}; +static const nsCSSPropertyID kBorderWidthIDs[] = { + eCSSProperty_border_top_width, + eCSSProperty_border_right_width, + eCSSProperty_border_bottom_width, + eCSSProperty_border_left_width +}; +static const nsCSSPropertyID kBorderColorIDs[] = { + eCSSProperty_border_top_color, + eCSSProperty_border_right_color, + eCSSProperty_border_bottom_color, + eCSSProperty_border_left_color +}; +static const nsCSSPropertyID kBorderRadiusIDs[] = { + eCSSProperty_border_top_left_radius, + eCSSProperty_border_top_right_radius, + eCSSProperty_border_bottom_right_radius, + eCSSProperty_border_bottom_left_radius +}; +static const nsCSSPropertyID kOutlineRadiusIDs[] = { + eCSSProperty__moz_outline_radius_topLeft, + eCSSProperty__moz_outline_radius_topRight, + eCSSProperty__moz_outline_radius_bottomRight, + eCSSProperty__moz_outline_radius_bottomLeft +}; + +void +CSSParserImpl::SaveInputState(CSSParserInputState& aState) +{ + aState.mToken = mToken; + aState.mHavePushBack = mHavePushBack; + mScanner->SavePosition(aState.mPosition); +} + +void +CSSParserImpl::RestoreSavedInputState(const CSSParserInputState& aState) +{ + mToken = aState.mToken; + mHavePushBack = aState.mHavePushBack; + mScanner->RestoreSavedPosition(aState.mPosition); +} + +bool +CSSParserImpl::ParseProperty(nsCSSPropertyID aPropID) +{ + // Can't use AutoRestore because it's a bitfield. + MOZ_ASSERT(!mHashlessColorQuirk, + "hashless color quirk should not be set"); + MOZ_ASSERT(!mUnitlessLengthQuirk, + "unitless length quirk should not be set"); + MOZ_ASSERT(aPropID != eCSSPropertyExtra_variable); + + if (mNavQuirkMode) { + mHashlessColorQuirk = + nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_HASHLESS_COLOR_QUIRK); + mUnitlessLengthQuirk = + nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_UNITLESS_LENGTH_QUIRK); + } + + // Save the current input state so that we can restore it later if we + // have to re-parse the property value as a variable-reference-containing + // token stream. + CSSParserInputState stateBeforeProperty; + SaveInputState(stateBeforeProperty); + mScanner->ClearSeenVariableReference(); + + NS_ASSERTION(aPropID < eCSSProperty_COUNT, "index out of range"); + bool allowVariables = true; + bool result; + switch (nsCSSProps::PropertyParseType(aPropID)) { + case CSS_PROPERTY_PARSE_INACCESSIBLE: { + // The user can't use these + REPORT_UNEXPECTED(PEInaccessibleProperty2); + allowVariables = false; + result = false; + break; + } + case CSS_PROPERTY_PARSE_FUNCTION: { + result = ParsePropertyByFunction(aPropID); + break; + } + case CSS_PROPERTY_PARSE_VALUE: { + result = false; + nsCSSValue value; + if (ParseSingleValueProperty(value, aPropID) == CSSParseResult::Ok) { + AppendValue(aPropID, value); + result = true; + } + // XXX Report errors? + break; + } + case CSS_PROPERTY_PARSE_VALUE_LIST: { + result = ParseValueList(aPropID); + break; + } + default: { + result = false; + allowVariables = false; + MOZ_ASSERT(false, + "Property's flags field in nsCSSPropList.h is missing " + "one of the CSS_PROPERTY_PARSE_* constants"); + break; + } + } + + if (result) { + // We need to call ExpectEndProperty() to decide whether to reparse + // with variables. This is needed because the property parsing may + // have stopped upon finding a variable (e.g., 'margin: 1px var(a)') + // in a way that future variable substitutions will be valid, or + // because it parsed everything that's possible but we still want to + // act as though the property contains variables even though we know + // the substitution will never work (e.g., for 'margin: 1px 2px 3px + // 4px 5px var(a)'). + // + // It would be nice to find a better solution here + // (and for the SkipUntilOneOf below), though, that doesn't depend + // on using what we don't accept for doing parsing correctly. + if (!ExpectEndProperty()) { + result = false; + } + } + + bool seenVariable = mScanner->SeenVariableReference() || + (stateBeforeProperty.mHavePushBack && + stateBeforeProperty.mToken.mType == eCSSToken_Function && + stateBeforeProperty.mToken.mIdent.LowerCaseEqualsLiteral("var")); + bool parseAsTokenStream; + + if (!result && allowVariables) { + parseAsTokenStream = true; + if (!seenVariable) { + // We might have stopped parsing the property before its end and before + // finding a variable reference. Keep checking until the end of the + // property. + CSSParserInputState stateAtError; + SaveInputState(stateAtError); + + const char16_t stopChars[] = { ';', '!', '}', ')', 0 }; + SkipUntilOneOf(stopChars); + UngetToken(); + parseAsTokenStream = mScanner->SeenVariableReference(); + + if (!parseAsTokenStream) { + // If we parsed to the end of the propery and didn't find any variable + // references, then the real position we want to report the error at + // is |stateAtError|. + RestoreSavedInputState(stateAtError); + } + } + } else { + parseAsTokenStream = false; + } + + if (parseAsTokenStream) { + // Go back to the start of the property value and parse it to make sure + // its variable references are syntactically valid and is otherwise + // balanced. + RestoreSavedInputState(stateBeforeProperty); + + if (!mInSupportsCondition) { + mScanner->StartRecording(); + } + + CSSVariableDeclarations::Type type; + bool dropBackslash; + nsString impliedCharacters; + nsCSSValue value; + if (ParseValueWithVariables(&type, &dropBackslash, impliedCharacters, + nullptr, nullptr)) { + MOZ_ASSERT(type == CSSVariableDeclarations::eTokenStream, + "a non-custom property reparsed since it contained variable " + "references should not have been 'initial' or 'inherit'"); + + nsString propertyValue; + + if (!mInSupportsCondition) { + // If we are in an @supports condition, we don't need to store the + // actual token stream on the nsCSSValue. + mScanner->StopRecording(propertyValue); + if (dropBackslash) { + MOZ_ASSERT(!propertyValue.IsEmpty() && + propertyValue[propertyValue.Length() - 1] == '\\'); + propertyValue.Truncate(propertyValue.Length() - 1); + } + propertyValue.Append(impliedCharacters); + } + + if (mHavePushBack) { + // If we came to the end of a property value that had a variable + // reference and a token was pushed back, then it would have been + // ended by '!', ')', ';', ']' or '}'. We should remove it from the + // recorded property value. + MOZ_ASSERT(mToken.IsSymbol('!') || + mToken.IsSymbol(')') || + mToken.IsSymbol(';') || + mToken.IsSymbol(']') || + mToken.IsSymbol('}')); + if (!mInSupportsCondition) { + MOZ_ASSERT(!propertyValue.IsEmpty()); + MOZ_ASSERT(propertyValue[propertyValue.Length() - 1] == + mToken.mSymbol); + propertyValue.Truncate(propertyValue.Length() - 1); + } + } + + if (!mInSupportsCondition) { + if (nsCSSProps::IsShorthand(aPropID)) { + // If this is a shorthand property, we store the token stream on each + // of its corresponding longhand properties. + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aPropID, EnabledState()) { + nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream; + tokenStream->mPropertyID = *p; + tokenStream->mShorthandPropertyID = aPropID; + tokenStream->mTokenStream = propertyValue; + tokenStream->mBaseURI = mBaseURI; + tokenStream->mSheetURI = mSheetURI; + tokenStream->mSheetPrincipal = mSheetPrincipal; + // XXX Should store sheet here (see bug 952338). + // tokenStream->mSheet = mSheet; + tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber(); + tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset(); + value.SetTokenStreamValue(tokenStream); + AppendValue(*p, value); + } + } else { + nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream; + tokenStream->mPropertyID = aPropID; + tokenStream->mTokenStream = propertyValue; + tokenStream->mBaseURI = mBaseURI; + tokenStream->mSheetURI = mSheetURI; + tokenStream->mSheetPrincipal = mSheetPrincipal; + // XXX Should store sheet here (see bug 952338). + // tokenStream->mSheet = mSheet; + tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber(); + tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset(); + value.SetTokenStreamValue(tokenStream); + AppendValue(aPropID, value); + } + } + result = true; + } else { + if (!mInSupportsCondition) { + mScanner->StopRecording(); + } + } + } + + if (mNavQuirkMode) { + mHashlessColorQuirk = false; + mUnitlessLengthQuirk = false; + } + + return result; +} + +bool +CSSParserImpl::ParsePropertyByFunction(nsCSSPropertyID aPropID) +{ + switch (aPropID) { // handle shorthand or multiple properties + case eCSSProperty_place_content: + return ParsePlaceContent(); + case eCSSProperty_place_items: + return ParsePlaceItems(); + case eCSSProperty_place_self: + return ParsePlaceSelf(); + case eCSSProperty_background: + return ParseImageLayers(nsStyleImageLayers::kBackgroundLayerTable); + case eCSSProperty_background_repeat: + return ParseImageLayerRepeat(eCSSProperty_background_repeat); + case eCSSProperty_background_position: + return ParseImageLayerPosition(nsStyleImageLayers::kBackgroundLayerTable); + case eCSSProperty_background_position_x: + case eCSSProperty_background_position_y: + return ParseImageLayerPositionCoord(aPropID, + aPropID == eCSSProperty_background_position_x); + case eCSSProperty_background_size: + return ParseImageLayerSize(eCSSProperty_background_size); + case eCSSProperty_border: + return ParseBorderSide(kBorderTopIDs, true); + case eCSSProperty_border_color: + return ParseBorderColor(); + case eCSSProperty_border_spacing: + return ParseBorderSpacing(); + case eCSSProperty_border_style: + return ParseBorderStyle(); + case eCSSProperty_border_block_end: + return ParseBorderSide(kBorderBlockEndIDs, false); + case eCSSProperty_border_block_start: + return ParseBorderSide(kBorderBlockStartIDs, false); + case eCSSProperty_border_bottom: + return ParseBorderSide(kBorderBottomIDs, false); + case eCSSProperty_border_inline_end: + return ParseBorderSide(kBorderInlineEndIDs, false); + case eCSSProperty_border_inline_start: + return ParseBorderSide(kBorderInlineStartIDs, false); + case eCSSProperty_border_left: + return ParseBorderSide(kBorderLeftIDs, false); + case eCSSProperty_border_right: + return ParseBorderSide(kBorderRightIDs, false); + case eCSSProperty_border_top: + return ParseBorderSide(kBorderTopIDs, false); + case eCSSProperty_border_bottom_colors: + case eCSSProperty_border_left_colors: + case eCSSProperty_border_right_colors: + case eCSSProperty_border_top_colors: + return ParseBorderColors(aPropID); + case eCSSProperty_border_image_slice: + return ParseBorderImageSlice(true, nullptr); + case eCSSProperty_border_image_width: + return ParseBorderImageWidth(true); + case eCSSProperty_border_image_outset: + return ParseBorderImageOutset(true); + case eCSSProperty_border_image_repeat: + return ParseBorderImageRepeat(true); + case eCSSProperty_border_image: + return ParseBorderImage(); + case eCSSProperty_border_width: + return ParseBorderWidth(); + case eCSSProperty_border_radius: + return ParseBoxCornerRadii(kBorderRadiusIDs); + case eCSSProperty__moz_outline_radius: + return ParseBoxCornerRadii(kOutlineRadiusIDs); + + case eCSSProperty_border_top_left_radius: + case eCSSProperty_border_top_right_radius: + case eCSSProperty_border_bottom_right_radius: + case eCSSProperty_border_bottom_left_radius: + case eCSSProperty__moz_outline_radius_topLeft: + case eCSSProperty__moz_outline_radius_topRight: + case eCSSProperty__moz_outline_radius_bottomRight: + case eCSSProperty__moz_outline_radius_bottomLeft: + return ParseBoxCornerRadius(aPropID); + + case eCSSProperty_box_shadow: + case eCSSProperty_text_shadow: + return ParseShadowList(aPropID); + + case eCSSProperty_clip: + return ParseRect(eCSSProperty_clip); + case eCSSProperty_columns: + return ParseColumns(); + case eCSSProperty_column_rule: + return ParseBorderSide(kColumnRuleIDs, false); + case eCSSProperty_content: + return ParseContent(); + case eCSSProperty_counter_increment: + case eCSSProperty_counter_reset: + return ParseCounterData(aPropID); + case eCSSProperty_cursor: + return ParseCursor(); + case eCSSProperty_filter: + return ParseFilter(); + case eCSSProperty_flex: + return ParseFlex(); + case eCSSProperty_flex_flow: + return ParseFlexFlow(); + case eCSSProperty_font: + return ParseFont(); + case eCSSProperty_font_variant: + return ParseFontVariant(); + case eCSSProperty_grid_auto_flow: + return ParseGridAutoFlow(); + case eCSSProperty_grid_auto_columns: + case eCSSProperty_grid_auto_rows: + return ParseGridAutoColumnsRows(aPropID); + case eCSSProperty_grid_template_areas: + return ParseGridTemplateAreas(); + case eCSSProperty_grid_template_columns: + case eCSSProperty_grid_template_rows: + return ParseGridTemplateColumnsRows(aPropID); + case eCSSProperty_grid_template: + return ParseGridTemplate(); + case eCSSProperty_grid: + return ParseGrid(); + case eCSSProperty_grid_column_start: + case eCSSProperty_grid_column_end: + case eCSSProperty_grid_row_start: + case eCSSProperty_grid_row_end: + return ParseGridColumnRowStartEnd(aPropID); + case eCSSProperty_grid_column: + return ParseGridColumnRow(eCSSProperty_grid_column_start, + eCSSProperty_grid_column_end); + case eCSSProperty_grid_row: + return ParseGridColumnRow(eCSSProperty_grid_row_start, + eCSSProperty_grid_row_end); + case eCSSProperty_grid_area: + return ParseGridArea(); + case eCSSProperty_grid_gap: + return ParseGridGap(); + case eCSSProperty_image_region: + return ParseRect(eCSSProperty_image_region); + case eCSSProperty_align_content: + case eCSSProperty_justify_content: + return ParseAlignJustifyContent(aPropID); + case eCSSProperty_align_items: + return ParseAlignItems(); + case eCSSProperty_align_self: + case eCSSProperty_justify_self: + return ParseAlignJustifySelf(aPropID); + case eCSSProperty_initial_letter: + return ParseInitialLetter(); + case eCSSProperty_justify_items: + return ParseJustifyItems(); + case eCSSProperty_list_style: + return ParseListStyle(); + case eCSSProperty_margin: + return ParseMargin(); + case eCSSProperty_object_position: + return ParseObjectPosition(); + case eCSSProperty_outline: + return ParseOutline(); + case eCSSProperty_overflow: + return ParseOverflow(); + case eCSSProperty_padding: + return ParsePadding(); + case eCSSProperty_quotes: + return ParseQuotes(); + case eCSSProperty_text_decoration: + return ParseTextDecoration(); + case eCSSProperty_text_emphasis: + return ParseTextEmphasis(); + case eCSSProperty_will_change: + return ParseWillChange(); + case eCSSProperty_transform: + return ParseTransform(false); + case eCSSProperty__moz_transform: + return ParseTransform(true); + case eCSSProperty_transform_origin: + return ParseTransformOrigin(false); + case eCSSProperty_perspective_origin: + return ParseTransformOrigin(true); + case eCSSProperty_transition: + return ParseTransition(); + case eCSSProperty_animation: + return ParseAnimation(); + case eCSSProperty_transition_property: + return ParseTransitionProperty(); + case eCSSProperty_fill: + case eCSSProperty_stroke: + return ParsePaint(aPropID); + case eCSSProperty_stroke_dasharray: + return ParseDasharray(); + case eCSSProperty_marker: + return ParseMarker(); + case eCSSProperty_paint_order: + return ParsePaintOrder(); + case eCSSProperty_scroll_snap_type: + return ParseScrollSnapType(); +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND + case eCSSProperty_mask: + return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable); + case eCSSProperty_mask_repeat: + return ParseImageLayerRepeat(eCSSProperty_mask_repeat); + case eCSSProperty_mask_position: + return ParseImageLayerPosition(nsStyleImageLayers::kMaskLayerTable); + case eCSSProperty_mask_position_x: + case eCSSProperty_mask_position_y: + return ParseImageLayerPositionCoord(aPropID, + aPropID == eCSSProperty_mask_position_x); + case eCSSProperty_mask_size: + return ParseImageLayerSize(eCSSProperty_mask_size); +#endif + case eCSSProperty__webkit_text_stroke: + return ParseWebkitTextStroke(); + case eCSSProperty_all: + return ParseAll(); + default: + MOZ_ASSERT(false, "should not be called"); + return false; + } +} + +// Bits used in determining which background position info we have +#define BG_CENTER NS_STYLE_IMAGELAYER_POSITION_CENTER +#define BG_TOP NS_STYLE_IMAGELAYER_POSITION_TOP +#define BG_BOTTOM NS_STYLE_IMAGELAYER_POSITION_BOTTOM +#define BG_LEFT NS_STYLE_IMAGELAYER_POSITION_LEFT +#define BG_RIGHT NS_STYLE_IMAGELAYER_POSITION_RIGHT +#define BG_CTB (BG_CENTER | BG_TOP | BG_BOTTOM) +#define BG_TB (BG_TOP | BG_BOTTOM) +#define BG_CLR (BG_CENTER | BG_LEFT | BG_RIGHT) +#define BG_LR (BG_LEFT | BG_RIGHT) + +CSSParseResult +CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue, + nsCSSPropertyID aPropID) +{ + if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) { + MOZ_ASSERT(false, "must only be called for longhand properties"); + return CSSParseResult::NotFound; + } + + MOZ_ASSERT(!nsCSSProps::PropHasFlags(aPropID, + CSS_PROPERTY_VALUE_PARSER_FUNCTION), + "must only be called for non-function-parsed properties"); + + uint32_t variant = nsCSSProps::ParserVariant(aPropID); + if (variant == 0) { + MOZ_ASSERT(false, "must only be called for variant-parsed properties"); + return CSSParseResult::NotFound; + } + + if (variant & ~(VARIANT_AHKLP | VARIANT_COLOR | VARIANT_CALC)) { + MOZ_ASSERT(false, "must only be called for properties that take certain " + "variants"); + return CSSParseResult::NotFound; + } + + const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID]; + uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID); + + return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions); +} + +bool +CSSParserImpl::ParseSingleValuePropertyByFunction(nsCSSValue& aValue, + nsCSSPropertyID aPropID) +{ + switch (aPropID) { + case eCSSProperty_clip_path: + return ParseClipPath(aValue); + case eCSSProperty_contain: + return ParseContain(aValue); + case eCSSProperty_font_family: + return ParseFamily(aValue); + case eCSSProperty_font_synthesis: + return ParseFontSynthesis(aValue); + case eCSSProperty_font_variant_alternates: + return ParseFontVariantAlternates(aValue); + case eCSSProperty_font_variant_east_asian: + return ParseFontVariantEastAsian(aValue); + case eCSSProperty_font_variant_ligatures: + return ParseFontVariantLigatures(aValue); + case eCSSProperty_font_variant_numeric: + return ParseFontVariantNumeric(aValue); + case eCSSProperty_font_feature_settings: + return ParseFontFeatureSettings(aValue); + case eCSSProperty_font_weight: + return ParseFontWeight(aValue); + case eCSSProperty_image_orientation: + return ParseImageOrientation(aValue); + case eCSSProperty_list_style_type: + return ParseListStyleType(aValue); + case eCSSProperty_scroll_snap_points_x: + return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x); + case eCSSProperty_scroll_snap_points_y: + return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y); + case eCSSProperty_scroll_snap_destination: + return ParseScrollSnapDestination(aValue); + case eCSSProperty_scroll_snap_coordinate: + return ParseScrollSnapCoordinate(aValue); + case eCSSProperty_shape_outside: + return ParseShapeOutside(aValue); + case eCSSProperty_text_align: + return ParseTextAlign(aValue); + case eCSSProperty_text_align_last: + return ParseTextAlignLast(aValue); + case eCSSProperty_text_decoration_line: + return ParseTextDecorationLine(aValue); + case eCSSProperty_text_combine_upright: + return ParseTextCombineUpright(aValue); + case eCSSProperty_text_emphasis_position: + return ParseTextEmphasisPosition(aValue); + case eCSSProperty_text_emphasis_style: + return ParseTextEmphasisStyle(aValue); + case eCSSProperty_text_overflow: + return ParseTextOverflow(aValue); + case eCSSProperty_touch_action: + return ParseTouchAction(aValue); + default: + MOZ_ASSERT(false, "should not reach here"); + return false; + } +} + +CSSParseResult +CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, + nsCSSPropertyID aPropID) +{ + if (aPropID == eCSSPropertyExtra_x_none_value) { + return ParseVariant(aValue, VARIANT_NONE | VARIANT_INHERIT, nullptr); + } + + if (aPropID == eCSSPropertyExtra_x_auto_value) { + return ParseVariant(aValue, VARIANT_AUTO | VARIANT_INHERIT, nullptr); + } + + if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) { + MOZ_ASSERT(false, "not a single value property"); + return CSSParseResult::NotFound; + } + + if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) { + uint32_t lineBefore, colBefore; + if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) { + // We're at EOF before parsing. + return CSSParseResult::NotFound; + } + + if (ParseSingleValuePropertyByFunction(aValue, aPropID)) { + return CSSParseResult::Ok; + } + + uint32_t lineAfter, colAfter; + if (!GetNextTokenLocation(true, &lineAfter, &colAfter) || + lineAfter != lineBefore || + colAfter != colBefore) { + // Any single token value that was invalid will have been pushed back, + // so GetNextTokenLocation encountering EOF means we failed while + // parsing a multi-token value. + return CSSParseResult::Error; + } + + return CSSParseResult::NotFound; + } + + uint32_t variant = nsCSSProps::ParserVariant(aPropID); + if (variant == 0) { + MOZ_ASSERT(false, "not a single value property"); + return CSSParseResult::NotFound; + } + + const KTableEntry* kwtable = nsCSSProps::kKeywordTableTable[aPropID]; + uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID); + return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions); +} + +// font-descriptor: descriptor ':' value ';' +// caller has advanced mToken to point at the descriptor +bool +CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID, + nsCSSValue& aValue) +{ + switch (aDescID) { + // These four are similar to the properties of the same name, + // possibly with more restrictions on the values they can take. + case eCSSFontDesc_Family: { + nsCSSValue value; + if (!ParseFamily(value) || + value.GetUnit() != eCSSUnit_FontFamilyList) + return false; + + // name can only be a single, non-generic name + const FontFamilyList* f = value.GetFontFamilyListValue(); + const nsTArray& fontlist = f->GetFontlist(); + + if (fontlist.Length() != 1 || !fontlist[0].IsNamed()) { + return false; + } + + aValue.SetStringValue(fontlist[0].mName, eCSSUnit_String); + return true; + } + + case eCSSFontDesc_Style: + // property is VARIANT_HMK|VARIANT_SYSFONT + return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL, + nsCSSProps::kFontStyleKTable); + + case eCSSFontDesc_Display: + return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD, + nsCSSProps::kFontDisplayKTable); + + case eCSSFontDesc_Weight: + return (ParseFontWeight(aValue) && + aValue.GetUnit() != eCSSUnit_Inherit && + aValue.GetUnit() != eCSSUnit_Initial && + aValue.GetUnit() != eCSSUnit_Unset && + (aValue.GetUnit() != eCSSUnit_Enumerated || + (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER && + aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER))); + + case eCSSFontDesc_Stretch: + // property is VARIANT_HK|VARIANT_SYSFONT + return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD, + nsCSSProps::kFontStretchKTable); + + // These two are unique to @font-face and have their own special grammar. + case eCSSFontDesc_Src: + return ParseFontSrc(aValue); + + case eCSSFontDesc_UnicodeRange: + return ParseFontRanges(aValue); + + case eCSSFontDesc_FontFeatureSettings: + return ParseFontFeatureSettings(aValue); + + case eCSSFontDesc_FontLanguageOverride: + return ParseSingleTokenVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, + nullptr); + + case eCSSFontDesc_UNKNOWN: + case eCSSFontDesc_COUNT: + NS_NOTREACHED("bad nsCSSFontDesc code"); + } + // explicitly do NOT have a default case to let the compiler + // help find missing descriptors + return false; +} + +static nsCSSValue +BoxPositionMaskToCSSValue(int32_t aMask, bool isX) +{ + int32_t val = NS_STYLE_IMAGELAYER_POSITION_CENTER; + if (isX) { + if (aMask & BG_LEFT) { + val = NS_STYLE_IMAGELAYER_POSITION_LEFT; + } + else if (aMask & BG_RIGHT) { + val = NS_STYLE_IMAGELAYER_POSITION_RIGHT; + } + } + else { + if (aMask & BG_TOP) { + val = NS_STYLE_IMAGELAYER_POSITION_TOP; + } + else if (aMask & BG_BOTTOM) { + val = NS_STYLE_IMAGELAYER_POSITION_BOTTOM; + } + } + + return nsCSSValue(val, eCSSUnit_Enumerated); +} + +bool +CSSParserImpl::ParseImageLayers(const nsCSSPropertyID aTable[]) +{ + nsAutoParseCompoundProperty compound(this); + + // background-color can only be set once, so it's not a list. + nsCSSValue color; + + // Check first for inherit/initial/unset. + if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) { + // must be alone + for (const nsCSSPropertyID* subprops = + nsCSSProps::SubpropertyEntryFor(aTable[nsStyleImageLayers::shorthand]); + *subprops != eCSSProperty_UNKNOWN; ++subprops) { + AppendValue(*subprops, color); + } + return true; + } + + nsCSSValue image, repeat, attachment, clip, origin, positionX, positionY, size, + composite, maskMode; + ImageLayersShorthandParseState state(color, image.SetListValue(), + repeat.SetPairListValue(), + attachment.SetListValue(), clip.SetListValue(), + origin.SetListValue(), + positionX.SetListValue(), positionY.SetListValue(), + size.SetPairListValue(), composite.SetListValue(), + maskMode.SetListValue()); + + for (;;) { + if (!ParseImageLayersItem(state, aTable)) { + return false; + } + + // If we saw a color, this must be the last item. + if (color.GetUnit() != eCSSUnit_Null) { + MOZ_ASSERT(aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN); + break; + } + + // If there's a comma, expect another item. + if (!ExpectSymbol(',', true)) { + break; + } + +#define APPENDNEXT(propID_, propMember_, propType_) \ + if (aTable[propID_] != eCSSProperty_UNKNOWN) { \ + propMember_->mNext = new propType_; \ + propMember_ = propMember_->mNext; \ + } + // Chain another entry on all the lists. + APPENDNEXT(nsStyleImageLayers::image, state.mImage, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::repeat, state.mRepeat, + nsCSSValuePairList); + APPENDNEXT(nsStyleImageLayers::clip, state.mClip, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::origin, state.mOrigin, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::positionX, state.mPositionX, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::positionY, state.mPositionY, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::size, state.mSize, + nsCSSValuePairList); + APPENDNEXT(nsStyleImageLayers::attachment, state.mAttachment, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::maskMode, state.mMode, + nsCSSValueList); + APPENDNEXT(nsStyleImageLayers::composite, state.mComposite, + nsCSSValueList); +#undef APPENDNEXT + } + + // If we get to this point without seeing a color, provide a default. + if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + if (color.GetUnit() == eCSSUnit_Null) { + color.SetIntegerColorValue(NS_RGBA(0,0,0,0), eCSSUnit_RGBAColor); + } + } + +#define APPENDVALUE(propID_, propValue_) \ + if (propID_ != eCSSProperty_UNKNOWN) { \ + AppendValue(propID_, propValue_); \ + } + + APPENDVALUE(aTable[nsStyleImageLayers::image], image); + APPENDVALUE(aTable[nsStyleImageLayers::repeat], repeat); + APPENDVALUE(aTable[nsStyleImageLayers::clip], clip); + APPENDVALUE(aTable[nsStyleImageLayers::origin], origin); + APPENDVALUE(aTable[nsStyleImageLayers::positionX], positionX); + APPENDVALUE(aTable[nsStyleImageLayers::positionY], positionY); + APPENDVALUE(aTable[nsStyleImageLayers::size], size); + APPENDVALUE(aTable[nsStyleImageLayers::color], color); + APPENDVALUE(aTable[nsStyleImageLayers::attachment], attachment); + APPENDVALUE(aTable[nsStyleImageLayers::maskMode], maskMode); + APPENDVALUE(aTable[nsStyleImageLayers::composite], composite); + +#undef APPENDVALUE + + return true; +} + +// Helper for ParseImageLayersItem. Returns true if the passed-in nsCSSToken is +// a function which is accepted for background-image. +bool +CSSParserImpl::IsFunctionTokenValidForImageLayerImage( + const nsCSSToken& aToken) const +{ + MOZ_ASSERT(aToken.mType == eCSSToken_Function, + "Should only be called for function-typed tokens"); + + const nsAString& funcName = aToken.mIdent; + + return funcName.LowerCaseEqualsLiteral("linear-gradient") || + funcName.LowerCaseEqualsLiteral("radial-gradient") || + funcName.LowerCaseEqualsLiteral("repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("repeating-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-image-rect") || + funcName.LowerCaseEqualsLiteral("-moz-element") || + ((sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService()) && + (funcName.LowerCaseEqualsLiteral("-webkit-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient"))); +} + +// Parse one item of the background shorthand property. +bool +CSSParserImpl::ParseImageLayersItem( + CSSParserImpl::ImageLayersShorthandParseState& aState, + const nsCSSPropertyID aTable[]) +{ + // Fill in the values that the shorthand will set if we don't find + // other values. + aState.mImage->mValue.SetNoneValue(); + aState.mAttachment->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL, + eCSSUnit_Enumerated); + aState.mClip->mValue.SetIntValue(NS_STYLE_IMAGELAYER_CLIP_BORDER, + eCSSUnit_Enumerated); + + aState.mRepeat->mXValue.SetIntValue(NS_STYLE_IMAGELAYER_REPEAT_REPEAT, + eCSSUnit_Enumerated); + aState.mRepeat->mYValue.Reset(); + + RefPtr positionXArr = nsCSSValue::Array::Create(2); + RefPtr positionYArr = nsCSSValue::Array::Create(2); + aState.mPositionX->mValue.SetArrayValue(positionXArr, eCSSUnit_Array); + aState.mPositionY->mValue.SetArrayValue(positionYArr, eCSSUnit_Array); + + if (eCSSProperty_mask == aTable[nsStyleImageLayers::shorthand]) { + aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_BORDER, + eCSSUnit_Enumerated); + } else { + aState.mOrigin->mValue.SetIntValue(NS_STYLE_IMAGELAYER_ORIGIN_PADDING, + eCSSUnit_Enumerated); + } + positionXArr->Item(1).SetPercentValue(0.0f); + positionYArr->Item(1).SetPercentValue(0.0f); + + aState.mSize->mXValue.SetAutoValue(); + aState.mSize->mYValue.SetAutoValue(); + aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD, + eCSSUnit_Enumerated); + aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE, + eCSSUnit_Enumerated); + bool haveColor = false, + haveImage = false, + haveRepeat = false, + haveAttach = false, + havePositionAndSize = false, + haveOrigin = false, + haveComposite = false, + haveMode = false, + haveSomething = false; + + while (GetToken(true)) { + nsCSSTokenType tt = mToken.mType; + UngetToken(); // ...but we'll still cheat and use mToken + if (tt == eCSSToken_Symbol) { + // ExpectEndProperty only looks for symbols, and nothing else will + // show up as one. + break; + } + + if (tt == eCSSToken_Ident) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + int32_t dummy; + if (keyword == eCSSKeyword_inherit || + keyword == eCSSKeyword_initial || + keyword == eCSSKeyword_unset) { + return false; + } else if (keyword == eCSSKeyword_none) { + if (haveImage) + return false; + haveImage = true; + if (ParseSingleValueProperty(aState.mImage->mValue, + aTable[nsStyleImageLayers::image]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (aTable[nsStyleImageLayers::attachment] != + eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerAttachmentKTable, dummy)) { + if (haveAttach) + return false; + haveAttach = true; + if (ParseSingleValueProperty(aState.mAttachment->mValue, + aTable[nsStyleImageLayers::attachment]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerRepeatKTable, dummy)) { + if (haveRepeat) + return false; + haveRepeat = true; + nsCSSValuePair scratch; + if (!ParseImageLayerRepeatValues(scratch)) { + NS_NOTREACHED("should be able to parse"); + return false; + } + aState.mRepeat->mXValue = scratch.mXValue; + aState.mRepeat->mYValue = scratch.mYValue; + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerPositionKTable, dummy)) { + if (havePositionAndSize) + return false; + havePositionAndSize = true; + + if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue, + aState.mPositionY->mValue)) { + return false; + } + if (ExpectSymbol('/', true)) { + nsCSSValuePair scratch; + if (!ParseImageLayerSizeValues(scratch)) { + return false; + } + aState.mSize->mXValue = scratch.mXValue; + aState.mSize->mYValue = scratch.mYValue; + } + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerOriginKTable, dummy)) { + if (haveOrigin) + return false; + haveOrigin = true; + if (ParseSingleValueProperty(aState.mOrigin->mValue, + aTable[nsStyleImageLayers::origin]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + + // The spec allows a second box value (for background-clip), + // immediately following the first one (for background-origin). + +#ifdef DEBUG + for (size_t i = 0; nsCSSProps::kImageLayerOriginKTable[i].mValue != -1; i++) { + // For each keyword & value in kOriginKTable, ensure that + // kBackgroundKTable has a matching entry at the same position. + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mKeyword == + nsCSSProps::kBackgroundClipKTable[i].mKeyword); + MOZ_ASSERT(nsCSSProps::kImageLayerOriginKTable[i].mValue == + nsCSSProps::kBackgroundClipKTable[i].mValue); + } +#endif + static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == + NS_STYLE_IMAGELAYER_ORIGIN_BORDER && + NS_STYLE_IMAGELAYER_CLIP_PADDING == + NS_STYLE_IMAGELAYER_ORIGIN_PADDING && + NS_STYLE_IMAGELAYER_CLIP_CONTENT == + NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, + "bg-clip and bg-origin style constants must agree"); + + CSSParseResult result = + ParseSingleValueProperty(aState.mClip->mValue, + aTable[nsStyleImageLayers::clip]); + MOZ_ASSERT(result != CSSParseResult::Error, + "how can failing to parse a single background-clip value " + "consume tokens?"); + if (result == CSSParseResult::NotFound) { + // When exactly one value is set, it is used for both + // 'background-origin' and 'background-clip'. + // See assertions above showing these values are compatible. + aState.mClip->mValue = aState.mOrigin->mValue; + } + } else if (aTable[nsStyleImageLayers::composite] != eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerCompositeKTable, dummy)) { + if (haveComposite) + return false; + haveComposite = true; + if (ParseSingleValueProperty(aState.mComposite->mValue, + aTable[nsStyleImageLayers::composite]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (aTable[nsStyleImageLayers::maskMode] != eCSSProperty_UNKNOWN && + nsCSSProps::FindKeyword(keyword, + nsCSSProps::kImageLayerModeKTable, dummy)) { + if (haveMode) + return false; + haveMode = true; + if (ParseSingleValueProperty(aState.mMode->mValue, + aTable[nsStyleImageLayers::maskMode]) != + CSSParseResult::Ok) { + NS_NOTREACHED("should be able to parse"); + return false; + } + } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + if (haveColor) + return false; + haveColor = true; + if (ParseSingleValueProperty(aState.mColor, + aTable[nsStyleImageLayers::color]) != + CSSParseResult::Ok) { + return false; + } + } else { + return false; + } + } else if (tt == eCSSToken_URL || + (tt == eCSSToken_Function && + IsFunctionTokenValidForImageLayerImage(mToken))) { + if (haveImage) + return false; + haveImage = true; + if (ParseSingleValueProperty(aState.mImage->mValue, + aTable[nsStyleImageLayers::image]) != + CSSParseResult::Ok) { + return false; + } + } else if (tt == eCSSToken_Dimension || + tt == eCSSToken_Number || + tt == eCSSToken_Percentage || + (tt == eCSSToken_Function && + (mToken.mIdent.LowerCaseEqualsLiteral("calc") || + mToken.mIdent.LowerCaseEqualsLiteral("-moz-calc")))) { + if (havePositionAndSize) + return false; + havePositionAndSize = true; + if (!ParsePositionValueSeparateCoords(aState.mPositionX->mValue, + aState.mPositionY->mValue)) { + return false; + } + if (ExpectSymbol('/', true)) { + nsCSSValuePair scratch; + if (!ParseImageLayerSizeValues(scratch)) { + return false; + } + aState.mSize->mXValue = scratch.mXValue; + aState.mSize->mYValue = scratch.mYValue; + } + } else if (aTable[nsStyleImageLayers::color] != eCSSProperty_UNKNOWN) { + if (haveColor) + return false; + haveColor = true; + // Note: This parses 'inherit', 'initial' and 'unset', but + // we've already checked for them, so it's ok. + if (ParseSingleValueProperty(aState.mColor, + aTable[nsStyleImageLayers::color]) != + CSSParseResult::Ok) { + return false; + } + } else { + return false; + } + + haveSomething = true; + } + + return haveSomething; +} + +// This function is very similar to ParseScrollSnapCoordinate, +// ParseImageLayerPosition, and ParseImageLayersSize. +bool +CSSParserImpl::ParseValueList(nsCSSPropertyID aPropID) +{ + // aPropID is a single value prop-id + nsCSSValue value; + // 'initial', 'inherit' and 'unset' stand alone, no list permitted. + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + nsCSSValueList* item = value.SetListValue(); + for (;;) { + if (ParseSingleValueProperty(item->mValue, aPropID) != + CSSParseResult::Ok) { + return false; + } + if (!ExpectSymbol(',', true)) { + break; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + } + } + AppendValue(aPropID, value); + return true; +} + +bool +CSSParserImpl::ParseImageLayerRepeat(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + // 'initial', 'inherit' and 'unset' stand alone, no list permitted. + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + nsCSSValuePair valuePair; + if (!ParseImageLayerRepeatValues(valuePair)) { + return false; + } + nsCSSValuePairList* item = value.SetPairListValue(); + for (;;) { + item->mXValue = valuePair.mXValue; + item->mYValue = valuePair.mYValue; + if (!ExpectSymbol(',', true)) { + break; + } + if (!ParseImageLayerRepeatValues(valuePair)) { + return false; + } + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + } + + AppendValue(aPropID, value); + return true; +} + +bool +CSSParserImpl::ParseImageLayerRepeatValues(nsCSSValuePair& aValue) +{ + nsCSSValue& xValue = aValue.mXValue; + nsCSSValue& yValue = aValue.mYValue; + + if (ParseEnum(xValue, nsCSSProps::kImageLayerRepeatKTable)) { + int32_t value = xValue.GetIntValue(); + // For single values set yValue as eCSSUnit_Null. + if (value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X || + value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y || + !ParseEnum(yValue, nsCSSProps::kImageLayerRepeatPartKTable)) { + // the caller will fail cases like "repeat-x no-repeat" + // by expecting a list separator or an end property. + yValue.Reset(); + } + return true; + } + + return false; +} + +bool +CSSParserImpl::ParseImageLayerPosition(const nsCSSPropertyID aTable[]) +{ + // 'initial', 'inherit' and 'unset' stand alone, no list permitted. + nsCSSValue position; + if (ParseSingleTokenVariant(position, VARIANT_INHERIT, nullptr)) { + AppendValue(aTable[nsStyleImageLayers::positionX], position); + AppendValue(aTable[nsStyleImageLayers::positionY], position); + return true; + } + + nsCSSValue itemValueX; + nsCSSValue itemValueY; + if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) { + return false; + } + + nsCSSValue valueX; + nsCSSValue valueY; + nsCSSValueList* itemX = valueX.SetListValue(); + nsCSSValueList* itemY = valueY.SetListValue(); + for (;;) { + itemX->mValue = itemValueX; + itemY->mValue = itemValueY; + if (!ExpectSymbol(',', true)) { + break; + } + if (!ParsePositionValueSeparateCoords(itemValueX, itemValueY)) { + return false; + } + itemX->mNext = new nsCSSValueList; + itemY->mNext = new nsCSSValueList; + itemX = itemX->mNext; + itemY = itemY->mNext; + } + AppendValue(aTable[nsStyleImageLayers::positionX], valueX); + AppendValue(aTable[nsStyleImageLayers::positionY], valueY); + return true; +} + +bool +CSSParserImpl::ParseImageLayerPositionCoord(nsCSSPropertyID aPropID, bool aIsHorizontal) +{ + nsCSSValue value; + // 'initial', 'inherit' and 'unset' stand alone, no list permitted. + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + nsCSSValue itemValue; + if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) { + return false; + } + nsCSSValueList* item = value.SetListValue(); + for (;;) { + item->mValue = itemValue; + if (!ExpectSymbol(',', true)) { + break; + } + if (!ParseImageLayerPositionCoordItem(itemValue, aIsHorizontal)) { + return false; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + } + } + AppendValue(aPropID, value); + return true; +} + +/** + * BoxPositionMaskToCSSValue and ParseBoxPositionValues are used + * for parsing the CSS 2.1 background-position syntax (which has at + * most two values). (Compare to the css3-background syntax which + * takes up to four values.) Some current CSS specifications that + * use background-position-like syntax still use this old syntax. + ** + * Parses two values that correspond to positions in a box. These can be + * values corresponding to percentages of the box, raw offsets, or keywords + * like "top," "left center," etc. + * + * @param aOut The nsCSSValuePair in which to place the result. + * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are + * legal values + * @param aAllowExplicitCenter If true, 'center' is a legal value + * @return Whether or not the operation succeeded. + */ +bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, + bool aAcceptsInherit, + bool aAllowExplicitCenter) +{ + // First try a percentage or a length value + nsCSSValue &xValue = aOut.mXValue, + &yValue = aOut.mYValue; + int32_t variantMask = + (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC; + CSSParseResult result = ParseVariant(xValue, variantMask, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + if (eCSSUnit_Inherit == xValue.GetUnit() || + eCSSUnit_Initial == xValue.GetUnit() || + eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset + yValue = xValue; + return true; + } + // We have one percentage/length/calc. Get the optional second + // percentage/length/calc/keyword. + result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + // We have two numbers + return true; + } + + if (ParseEnum(yValue, nsCSSProps::kImageLayerPositionKTable)) { + int32_t yVal = yValue.GetIntValue(); + if (!(yVal & BG_CTB)) { + // The second keyword can only be 'center', 'top', or 'bottom' + return false; + } + yValue = BoxPositionMaskToCSSValue(yVal, false); + return true; + } + + // If only one percentage or length value is given, it sets the + // horizontal position only, and the vertical position will be 50%. + yValue.SetPercentValue(0.5f); + return true; + } + + // Now try keywords. We do this manually to allow for the first + // appearance of "center" to apply to the either the x or y + // position (it's ambiguous so we have to disambiguate). Each + // allowed keyword value is assigned it's own bit. We don't allow + // any duplicate keywords other than center. We try to get two + // keywords but it's okay if there is only one. + int32_t mask = 0; + if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) { + int32_t bit = xValue.GetIntValue(); + mask |= bit; + if (ParseEnum(xValue, nsCSSProps::kImageLayerPositionKTable)) { + bit = xValue.GetIntValue(); + if (mask & (bit & ~BG_CENTER)) { + // Only the 'center' keyword can be duplicated. + return false; + } + mask |= bit; + } + else { + // Only one keyword. See if we have a length, percentage, or calc. + result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + if (!(mask & BG_CLR)) { + // The first keyword can only be 'center', 'left', or 'right' + return false; + } + + xValue = BoxPositionMaskToCSSValue(mask, true); + return true; + } + } + } + + // Check for bad input. Bad input consists of no matching keywords, + // or pairs of x keywords or pairs of y keywords. + if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) || + (mask == (BG_LEFT | BG_RIGHT)) || + (!aAllowExplicitCenter && (mask & BG_CENTER))) { + return false; + } + + // Create style values + xValue = BoxPositionMaskToCSSValue(mask, true); + yValue = BoxPositionMaskToCSSValue(mask, false); + return true; +} + +// Parses a CSS value, for e.g. the 'background-position' property. +// Spec reference: http://www.w3.org/TR/css3-background/#ltpositiongt +// Invariants: +// - Always produces a four-value array on a successful parse. +// - The values are: X edge, X offset, Y edge, Y offset. +// - Edges are always keywords or null. +// - A |center| edge will not have an offset. +bool +CSSParserImpl::ParsePositionValue(nsCSSValue& aOut) +{ + RefPtr value = nsCSSValue::Array::Create(4); + aOut.SetArrayValue(value, eCSSUnit_Array); + + // The following clarifies organisation of the array. + nsCSSValue &xEdge = value->Item(0), + &xOffset = value->Item(1), + &yEdge = value->Item(2), + &yOffset = value->Item(3); + + // Parse all the values into the array. + uint32_t valueCount = 0; + for (int32_t i = 0; i < 4; i++) { + CSSParseResult result = + ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD, + nsCSSProps::kImageLayerPositionKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + break; + } + ++valueCount; + } + + switch (valueCount) { + case 4: + // "If three or four values are given, then each or + // represents an offset and must be preceded by a keyword, which specifies + // from which edge the offset is given." + if (eCSSUnit_Enumerated != xEdge.GetUnit() || + BG_CENTER == xEdge.GetIntValue() || + eCSSUnit_Enumerated == xOffset.GetUnit() || + eCSSUnit_Enumerated != yEdge.GetUnit() || + BG_CENTER == yEdge.GetIntValue() || + eCSSUnit_Enumerated == yOffset.GetUnit()) { + return false; + } + break; + case 3: + // "If three or four values are given, then each or + // represents an offset and must be preceded by a keyword, which specifies + // from which edge the offset is given." ... "If three values are given, + // the missing offset is assumed to be zero." + if (eCSSUnit_Enumerated != value->Item(1).GetUnit()) { + // keyword offset keyword + // Second value is non-keyword, thus first value must be a non-center + // keyword. + if (eCSSUnit_Enumerated != value->Item(0).GetUnit() || + BG_CENTER == value->Item(0).GetIntValue()) { + return false; + } + + // Remaining value must be a keyword. + if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) { + return false; + } + + yOffset.Reset(); // Everything else is in the correct position. + } else if (eCSSUnit_Enumerated != value->Item(2).GetUnit()) { + // keyword keyword offset + // Third value is non-keyword, thus second value must be non-center + // keyword. + if (BG_CENTER == value->Item(1).GetIntValue()) { + return false; + } + + // Remaining value must be a keyword. + if (eCSSUnit_Enumerated != value->Item(0).GetUnit()) { + return false; + } + + // Move the values to the correct position in the array. + value->Item(3) = value->Item(2); // yOffset + value->Item(2) = value->Item(1); // yEdge + value->Item(1).Reset(); // xOffset + } else { + return false; + } + break; + case 2: + // "If two values are given and at least one value is not a keyword, then + // the first value represents the horizontal position (or offset) and the + // second represents the vertical position (or offset)" + if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) { + if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) { + // keyword keyword + value->Item(2) = value->Item(1); // move yEdge to correct position + xOffset.Reset(); + yOffset.Reset(); + } else { + // keyword offset + // First value must represent horizontal position. + if ((BG_TOP | BG_BOTTOM) & value->Item(0).GetIntValue()) { + return false; + } + value->Item(3) = value->Item(1); // move yOffset to correct position + xOffset.Reset(); + yEdge.Reset(); + } + } else { + if (eCSSUnit_Enumerated == value->Item(1).GetUnit()) { + // offset keyword + // Second value must represent vertical position. + if ((BG_LEFT | BG_RIGHT) & value->Item(1).GetIntValue()) { + return false; + } + value->Item(2) = value->Item(1); // move yEdge to correct position + value->Item(1) = value->Item(0); // move xOffset to correct position + xEdge.Reset(); + yOffset.Reset(); + } else { + // offset offset + value->Item(3) = value->Item(1); // move yOffset to correct position + value->Item(1) = value->Item(0); // move xOffset to correct position + xEdge.Reset(); + yEdge.Reset(); + } + } + break; + case 1: + // "If only one value is specified, the second value is assumed to be + // center." + if (eCSSUnit_Enumerated == value->Item(0).GetUnit()) { + xOffset.Reset(); + } else { + value->Item(1) = value->Item(0); // move xOffset to correct position + xEdge.Reset(); + } + yEdge.SetIntValue(NS_STYLE_IMAGELAYER_POSITION_CENTER, eCSSUnit_Enumerated); + yOffset.Reset(); + break; + default: + return false; + } + + // For compatibility with CSS2.1 code the edges can be unspecified. + // Unspecified edges are recorded as nullptr. + NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || + eCSSUnit_Null == xEdge.GetUnit()) && + (eCSSUnit_Enumerated == yEdge.GetUnit() || + eCSSUnit_Null == yEdge.GetUnit()) && + eCSSUnit_Enumerated != xOffset.GetUnit() && + eCSSUnit_Enumerated != yOffset.GetUnit(), + "Unexpected units"); + + // Keywords in first and second pairs can not both be vertical or + // horizontal keywords. (eg. left right, bottom top). Additionally, + // non-center keyword can not be duplicated (eg. left left). + int32_t xEdgeEnum = + xEdge.GetUnit() == eCSSUnit_Enumerated ? xEdge.GetIntValue() : 0; + int32_t yEdgeEnum = + yEdge.GetUnit() == eCSSUnit_Enumerated ? yEdge.GetIntValue() : 0; + if ((xEdgeEnum | yEdgeEnum) == (BG_LEFT | BG_RIGHT) || + (xEdgeEnum | yEdgeEnum) == (BG_TOP | BG_BOTTOM) || + (xEdgeEnum & yEdgeEnum & ~BG_CENTER)) { + return false; + } + + // The values could be in an order that is different than expected. + // eg. x contains vertical information, y contains horizontal information. + // Swap if incorrect order. + if (xEdgeEnum & (BG_TOP | BG_BOTTOM) || + yEdgeEnum & (BG_LEFT | BG_RIGHT)) { + nsCSSValue swapEdge = xEdge; + nsCSSValue swapOffset = xOffset; + xEdge = yEdge; + xOffset = yOffset; + yEdge = swapEdge; + yOffset = swapOffset; + } + + return true; +} + +static void +AdjustEdgeOffsetPairForBasicShape(nsCSSValue& aEdge, + nsCSSValue& aOffset, + uint8_t aDefaultEdge) +{ + // 0 length offsets are 0% + if (aOffset.IsLengthUnit() && aOffset.GetFloatValue() == 0.0) { + aOffset.SetPercentValue(0); + } + + // Default edge is top/left in the 4-value case + // In case of 1 or 0 values, the default is center, + // but ParsePositionValue already handles this case + if (eCSSUnit_Null == aEdge.GetUnit()) { + aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated); + } + // Default offset is 0% + if (eCSSUnit_Null == aOffset.GetUnit()) { + aOffset.SetPercentValue(0.0); + } + if (eCSSUnit_Enumerated == aEdge.GetUnit() && + eCSSUnit_Percent == aOffset.GetUnit()) { + switch (aEdge.GetIntValue()) { + case NS_STYLE_IMAGELAYER_POSITION_CENTER: + aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated); + MOZ_ASSERT(aOffset.GetPercentValue() == 0.0, + "center cannot be used with an offset"); + aOffset.SetPercentValue(0.5); + break; + case NS_STYLE_IMAGELAYER_POSITION_BOTTOM: + MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_TOP); + aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated); + aOffset.SetPercentValue(1 - aOffset.GetPercentValue()); + break; + case NS_STYLE_IMAGELAYER_POSITION_RIGHT: + MOZ_ASSERT(aDefaultEdge == NS_STYLE_IMAGELAYER_POSITION_LEFT); + aEdge.SetIntValue(aDefaultEdge, eCSSUnit_Enumerated); + aOffset.SetPercentValue(1 - aOffset.GetPercentValue()); + } + } +} + +// https://drafts.csswg.org/css-shapes/#basic-shape-serialization +// We set values to defaults while parsing for basic shapes +// Invariants: +// - Always produces a four-value array on a successful parse. +// - The values are: X edge, X offset, Y edge, Y offset +// - Edges are always keywords (not including center) +// - Offsets are nonnull +// - Percentage offsets have keywords folded into them, +// so "bottom 40%" or "right 20%" will not exist. +bool +CSSParserImpl::ParsePositionValueForBasicShape(nsCSSValue& aOut) +{ + if (!ParsePositionValue(aOut)) { + return false; + } + nsCSSValue::Array* value = aOut.GetArrayValue(); + nsCSSValue& xEdge = value->Item(0); + nsCSSValue& xOffset = value->Item(1); + nsCSSValue& yEdge = value->Item(2); + nsCSSValue& yOffset = value->Item(3); + // A keyword edge + percent offset pair can be contracted + // into the percentage with the default value in the edge. + // Offset lengths which are 0 can also be rewritten as 0% + AdjustEdgeOffsetPairForBasicShape(xEdge, xOffset, + NS_STYLE_IMAGELAYER_POSITION_LEFT); + AdjustEdgeOffsetPairForBasicShape(yEdge, yOffset, + NS_STYLE_IMAGELAYER_POSITION_TOP); + return true; +} + +bool +CSSParserImpl::ParsePositionValueSeparateCoords(nsCSSValue& aOutX, nsCSSValue& aOutY) +{ + nsCSSValue scratch; + if (!ParsePositionValue(scratch)) { + return false; + } + + // Separate the four values into two pairs of two values for X and Y. + RefPtr valueX = nsCSSValue::Array::Create(2); + RefPtr valueY = nsCSSValue::Array::Create(2); + aOutX.SetArrayValue(valueX, eCSSUnit_Array); + aOutY.SetArrayValue(valueY, eCSSUnit_Array); + + RefPtr value = scratch.GetArrayValue(); + valueX->Item(0) = value->Item(0); + valueX->Item(1) = value->Item(1); + valueY->Item(0) = value->Item(2); + valueY->Item(1) = value->Item(3); + return true; +} + +// Parses one item in a list of values for the 'background-position-x' or +// 'background-position-y' property. Does not support the start/end keywords. +// Spec reference: https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x +bool +CSSParserImpl::ParseImageLayerPositionCoordItem(nsCSSValue& aOut, bool aIsHorizontal) +{ + RefPtr value = nsCSSValue::Array::Create(2); + aOut.SetArrayValue(value, eCSSUnit_Array); + + nsCSSValue &edge = value->Item(0), + &offset = value->Item(1); + + nsCSSValue edgeOrOffset; + CSSParseResult result = + ParseVariant(edgeOrOffset, VARIANT_LPCALC | VARIANT_KEYWORD, + nsCSSProps::kImageLayerPositionKTable); + if (result != CSSParseResult::Ok) { + return false; + } + + if (edgeOrOffset.GetUnit() == eCSSUnit_Enumerated) { + edge = edgeOrOffset; + + // The edge can be followed by an optional offset. + result = ParseVariant(offset, VARIANT_LPCALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } + } else { + offset = edgeOrOffset; + } + + // Keywords for horizontal properties cannot be vertical keywords, and + // keywords for vertical properties cannot be horizontal keywords. + // Also, if an offset is specified, the edge cannot be center. + int32_t edgeEnum = + edge.GetUnit() == eCSSUnit_Enumerated ? edge.GetIntValue() : 0; + int32_t allowedKeywords = + (aIsHorizontal ? (BG_LEFT | BG_RIGHT) : (BG_TOP | BG_BOTTOM)) | + (offset.GetUnit() == eCSSUnit_Null ? BG_CENTER : 0); + if (edgeEnum & ~allowedKeywords) { + return false; + } + + NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() || + eCSSUnit_Null == edge.GetUnit()) && + eCSSUnit_Enumerated != offset.GetUnit(), + "Unexpected units"); + + return true; +} + +// This function is very similar to ParseScrollSnapCoordinate, +// ParseImageLayers, and ParseImageLayerPosition. +bool +CSSParserImpl::ParseImageLayerSize(nsCSSPropertyID aPropID) +{ + nsCSSValue value; + // 'initial', 'inherit' and 'unset' stand alone, no list permitted. + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + nsCSSValuePair valuePair; + if (!ParseImageLayerSizeValues(valuePair)) { + return false; + } + nsCSSValuePairList* item = value.SetPairListValue(); + for (;;) { + item->mXValue = valuePair.mXValue; + item->mYValue = valuePair.mYValue; + if (!ExpectSymbol(',', true)) { + break; + } + if (!ParseImageLayerSizeValues(valuePair)) { + return false; + } + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + } + AppendValue(aPropID, value); + return true; +} + +/** + * Parses two values that correspond to lengths for the background-size + * property. These can be one or two lengths (or the 'auto' keyword) or + * percentages corresponding to the element's dimensions or the single keywords + * 'contain' or 'cover'. 'initial', 'inherit' and 'unset' must be handled by + * the caller if desired. + * + * @param aOut The nsCSSValuePair in which to place the result. + * @return Whether or not the operation succeeded. + */ +#define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC) +bool CSSParserImpl::ParseImageLayerSizeValues(nsCSSValuePair &aOut) +{ + // First try a percentage or a length value + nsCSSValue &xValue = aOut.mXValue, + &yValue = aOut.mYValue; + CSSParseResult result = + ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + // We have one percentage/length/calc/auto. Get the optional second + // percentage/length/calc/keyword. + result = ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + // We have a second percentage/length/calc/auto. + return true; + } + + // If only one percentage or length value is given, it sets the + // horizontal size only, and the vertical size will be as if by 'auto'. + yValue.SetAutoValue(); + return true; + } + + // Now address 'contain' and 'cover'. + if (!ParseEnum(xValue, nsCSSProps::kImageLayerSizeKTable)) + return false; + yValue.Reset(); + return true; +} + +#undef BG_SIZE_VARIANT + +bool +CSSParserImpl::ParseBorderColor() +{ + return ParseBoxProperties(kBorderColorIDs); +} + +void +CSSParserImpl::SetBorderImageInitialValues() +{ + // border-image-source: none + nsCSSValue source; + source.SetNoneValue(); + AppendValue(eCSSProperty_border_image_source, source); + + // border-image-slice: 100% + nsCSSValue sliceBoxValue; + nsCSSRect& sliceBox = sliceBoxValue.SetRectValue(); + sliceBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Percent)); + nsCSSValue slice; + nsCSSValueList* sliceList = slice.SetListValue(); + sliceList->mValue = sliceBoxValue; + AppendValue(eCSSProperty_border_image_slice, slice); + + // border-image-width: 1 + nsCSSValue width; + nsCSSRect& widthBox = width.SetRectValue(); + widthBox.SetAllSidesTo(nsCSSValue(1.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_border_image_width, width); + + // border-image-outset: 0 + nsCSSValue outset; + nsCSSRect& outsetBox = outset.SetRectValue(); + outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_border_image_outset, outset); + + // border-image-repeat: repeat + nsCSSValue repeat; + nsCSSValuePair repeatPair; + repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, + eCSSUnit_Enumerated)); + repeat.SetPairValue(&repeatPair); + AppendValue(eCSSProperty_border_image_repeat, repeat); +} + +bool +CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit, + bool* aConsumedTokens) +{ + // border-image-slice: initial | [|]{1,4} && fill? + nsCSSValue value; + + if (aConsumedTokens) { + *aConsumedTokens = true; + } + + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + // Keywords "inherit", "initial" and "unset" can not be mixed, so we + // are done. + AppendValue(eCSSProperty_border_image_slice, value); + return true; + } + + // Try parsing "fill" value. + nsCSSValue imageSliceFillValue; + bool hasFill = ParseEnum(imageSliceFillValue, + nsCSSProps::kBorderImageSliceKTable); + + // Parse the box dimensions. + nsCSSValue imageSliceBoxValue; + if (!ParseGroupedBoxProperty(VARIANT_PN, imageSliceBoxValue, + CSS_PROPERTY_VALUE_NONNEGATIVE)) { + if (!hasFill && aConsumedTokens) { + *aConsumedTokens = false; + } + + return false; + } + + // Try parsing "fill" keyword again if the first time failed because keyword + // and slice dimensions can be in any order. + if (!hasFill) { + hasFill = ParseEnum(imageSliceFillValue, + nsCSSProps::kBorderImageSliceKTable); + } + + nsCSSValueList* borderImageSlice = value.SetListValue(); + // Put the box value into the list. + borderImageSlice->mValue = imageSliceBoxValue; + + if (hasFill) { + // Put the "fill" value into the list. + borderImageSlice->mNext = new nsCSSValueList; + borderImageSlice->mNext->mValue = imageSliceFillValue; + } + + AppendValue(eCSSProperty_border_image_slice, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit) +{ + // border-image-width: initial | [|||auto]{1,4} + nsCSSValue value; + + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + // Keywords "inherit", "initial" and "unset" can not be mixed, so we + // are done. + AppendValue(eCSSProperty_border_image_width, value); + return true; + } + + // Parse the box dimensions. + if (!ParseGroupedBoxProperty(VARIANT_ALPN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) { + return false; + } + + AppendValue(eCSSProperty_border_image_width, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit) +{ + // border-image-outset: initial | [|]{1,4} + nsCSSValue value; + + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + // Keywords "inherit", "initial" and "unset" can not be mixed, so we + // are done. + AppendValue(eCSSProperty_border_image_outset, value); + return true; + } + + // Parse the box dimensions. + if (!ParseGroupedBoxProperty(VARIANT_LN, value, CSS_PROPERTY_VALUE_NONNEGATIVE)) { + return false; + } + + AppendValue(eCSSProperty_border_image_outset, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit) +{ + nsCSSValue value; + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + // Keywords "inherit", "initial" and "unset" can not be mixed, so we + // are done. + AppendValue(eCSSProperty_border_image_repeat, value); + return true; + } + + nsCSSValuePair result; + if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) { + return false; + } + + // optional second keyword, defaults to first + if (!ParseEnum(result.mYValue, nsCSSProps::kBorderImageRepeatKTable)) { + result.mYValue = result.mXValue; + } + + value.SetPairValue(&result); + AppendValue(eCSSProperty_border_image_repeat, value); + return true; +} + +bool +CSSParserImpl::ParseBorderImage() +{ + nsAutoParseCompoundProperty compound(this); + + // border-image: inherit | initial | + // || + // + // [ / | + // / ? / ]? || + // + + nsCSSValue value; + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + AppendValue(eCSSProperty_border_image_source, value); + AppendValue(eCSSProperty_border_image_slice, value); + AppendValue(eCSSProperty_border_image_width, value); + AppendValue(eCSSProperty_border_image_outset, value); + AppendValue(eCSSProperty_border_image_repeat, value); + // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done. + return true; + } + + // No empty property. + if (CheckEndProperty()) { + return false; + } + + // Shorthand properties are required to set everything they can. + SetBorderImageInitialValues(); + + bool foundSource = false; + bool foundSliceWidthOutset = false; + bool foundRepeat = false; + + // This loop is used to handle the parsing of border-image properties which + // can appear in any order. + nsCSSValue imageSourceValue; + while (!CheckEndProperty()) { + // + if (!foundSource) { + CSSParseResult result = + ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + AppendValue(eCSSProperty_border_image_source, imageSourceValue); + foundSource = true; + continue; + } + } + + // + // ParseBorderImageSlice is weird. It may consume tokens and then return + // false, because it parses a property with two required components that + // can appear in either order. Since the tokens that were consumed cannot + // parse as anything else we care about, this isn't a problem. + if (!foundSliceWidthOutset) { + bool sliceConsumedTokens = false; + if (ParseBorderImageSlice(false, &sliceConsumedTokens)) { + foundSliceWidthOutset = true; + + // [ / ? + if (ExpectSymbol('/', true)) { + bool foundBorderImageWidth = ParseBorderImageWidth(false); + + // [ / + if (ExpectSymbol('/', true)) { + if (!ParseBorderImageOutset(false)) { + return false; + } + } else if (!foundBorderImageWidth) { + // If this part has an trailing slash, the whole declaration is + // invalid. + return false; + } + } + + continue; + } else { + // If we consumed some tokens for but did not + // successfully parse it, we have an error. + if (sliceConsumedTokens) { + return false; + } + } + } + + // + if (!foundRepeat && ParseBorderImageRepeat(false)) { + foundRepeat = true; + continue; + } + + return false; + } + + return true; +} + +bool +CSSParserImpl::ParseBorderSpacing() +{ + nsCSSValue xValue, yValue; + if (ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr) != + CSSParseResult::Ok) { + return false; + } + + // If we have one length, get the optional second length. + // set the second value equal to the first. + if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) { + if (ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, + nullptr) == CSSParseResult::Error) { + return false; + } + } + + if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) { + AppendValue(eCSSProperty_border_spacing, xValue); + } else { + nsCSSValue pair; + pair.SetPairValue(xValue, yValue); + AppendValue(eCSSProperty_border_spacing, pair); + } + return true; +} + +bool +CSSParserImpl::ParseBorderSide(const nsCSSPropertyID aPropIDs[], + bool aSetAllSides) +{ + const int32_t numProps = 3; + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, aPropIDs, numProps); + if (found < 1) { + return false; + } + + if ((found & 1) == 0) { // Provide default border-width + values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated); + } + if ((found & 2) == 0) { // Provide default border-style + values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated); + } + if ((found & 4) == 0) { // text color will be used + values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + + if (aSetAllSides) { + // Parsing "border" shorthand; set all four sides to the same thing + for (int32_t index = 0; index < 4; index++) { + NS_ASSERTION(numProps == 3, "This code needs updating"); + AppendValue(kBorderWidthIDs[index], values[0]); + AppendValue(kBorderStyleIDs[index], values[1]); + AppendValue(kBorderColorIDs[index], values[2]); + } + + static const nsCSSPropertyID kBorderColorsProps[] = { + eCSSProperty_border_top_colors, + eCSSProperty_border_right_colors, + eCSSProperty_border_bottom_colors, + eCSSProperty_border_left_colors + }; + + // Set the other properties that the border shorthand sets to their + // initial values. + nsCSSValue extraValue; + switch (values[0].GetUnit()) { + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + case eCSSUnit_Unset: + extraValue = values[0]; + // Set value of border-image properties to initial/inherit/unset + AppendValue(eCSSProperty_border_image_source, extraValue); + AppendValue(eCSSProperty_border_image_slice, extraValue); + AppendValue(eCSSProperty_border_image_width, extraValue); + AppendValue(eCSSProperty_border_image_outset, extraValue); + AppendValue(eCSSProperty_border_image_repeat, extraValue); + break; + default: + extraValue.SetNoneValue(); + SetBorderImageInitialValues(); + break; + } + NS_FOR_CSS_SIDES(side) { + AppendValue(kBorderColorsProps[side], extraValue); + } + } + else { + // Just set our one side + for (int32_t index = 0; index < numProps; index++) { + AppendValue(aPropIDs[index], values[index]); + } + } + return true; +} + +bool +CSSParserImpl::ParseBorderStyle() +{ + return ParseBoxProperties(kBorderStyleIDs); +} + +bool +CSSParserImpl::ParseBorderWidth() +{ + return ParseBoxProperties(kBorderWidthIDs); +} + +bool +CSSParserImpl::ParseBorderColors(nsCSSPropertyID aProperty) +{ + nsCSSValue value; + // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + nsCSSValueList *cur = value.SetListValue(); + for (;;) { + if (ParseVariant(cur->mValue, VARIANT_COLOR, nullptr) != + CSSParseResult::Ok) { + return false; + } + if (CheckEndProperty()) { + break; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(aProperty, value); + return true; +} + +// Parse the top level of a calc() expression. +bool +CSSParserImpl::ParseCalc(nsCSSValue &aValue, uint32_t aVariantMask) +{ + // Parsing calc expressions requires, in a number of cases, looking + // for a token that is *either* a value of the property or a number. + // This can be done without lookahead when we assume that the property + // values cannot themselves be numbers. + MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); + + bool oldUnitlessLengthQuirk = mUnitlessLengthQuirk; + mUnitlessLengthQuirk = false; + + // One-iteration loop so we can break to the error-handling case. + do { + // The toplevel of a calc() is always an nsCSSValue::Array of length 1. + RefPtr arr = nsCSSValue::Array::Create(1); + + if (!ParseCalcAdditiveExpression(arr->Item(0), aVariantMask)) + break; + + if (!ExpectSymbol(')', true)) + break; + + aValue.SetArrayValue(arr, eCSSUnit_Calc); + mUnitlessLengthQuirk = oldUnitlessLengthQuirk; + return true; + } while (false); + + SkipUntil(')'); + mUnitlessLengthQuirk = oldUnitlessLengthQuirk; + return false; +} + +// We optimize away the production given that +// ParseVariant consumes initial whitespace and we call +// ExpectSymbol(')') with true for aSkipWS. +// * If aVariantMask is VARIANT_NUMBER, this function parses the +// production. +// * If aVariantMask does not contain VARIANT_NUMBER, this function +// parses the production. +// * Otherwise (VARIANT_NUMBER and other bits) this function parses +// whichever one of the productions matches ***and modifies +// aVariantMask*** to reflect which one it has parsed by either +// removing VARIANT_NUMBER or removing all other bits. +// It does so iteratively, but builds the correct recursive +// data structure. +bool +CSSParserImpl::ParseCalcAdditiveExpression(nsCSSValue& aValue, + uint32_t& aVariantMask) +{ + MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); + nsCSSValue *storage = &aValue; + for (;;) { + bool haveWS; + if (!ParseCalcMultiplicativeExpression(*storage, aVariantMask, &haveWS)) + return false; + + if (!haveWS || !GetToken(false)) + return true; + nsCSSUnit unit; + if (mToken.IsSymbol('+')) { + unit = eCSSUnit_Calc_Plus; + } else if (mToken.IsSymbol('-')) { + unit = eCSSUnit_Calc_Minus; + } else { + UngetToken(); + return true; + } + if (!RequireWhitespace()) + return false; + + RefPtr arr = nsCSSValue::Array::Create(2); + arr->Item(0) = aValue; + storage = &arr->Item(1); + aValue.SetArrayValue(arr, unit); + } +} + +struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps, + public mozilla::css::CSSValueInputCalcOps +{ + result_type ComputeLeafValue(const nsCSSValue& aValue) + { + MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit"); + return aValue.GetFloatValue(); + } + + float ComputeNumber(const nsCSSValue& aValue) + { + return mozilla::css::ComputeCalc(aValue, *this); + } +}; + +// * If aVariantMask is VARIANT_NUMBER, this function parses the +// production. +// * If aVariantMask does not contain VARIANT_NUMBER, this function +// parses the production. +// * Otherwise (VARIANT_NUMBER and other bits) this function parses +// whichever one of the productions matches ***and modifies +// aVariantMask*** to reflect which one it has parsed by either +// removing VARIANT_NUMBER or removing all other bits. +// It does so iteratively, but builds the correct recursive data +// structure. +// This function always consumes *trailing* whitespace when it returns +// true; whether there was any such whitespace is returned in the +// aHadFinalWS parameter. +bool +CSSParserImpl::ParseCalcMultiplicativeExpression(nsCSSValue& aValue, + uint32_t& aVariantMask, + bool *aHadFinalWS) +{ + MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); + bool gotValue = false; // already got the part with the unit + bool afterDivision = false; + + nsCSSValue *storage = &aValue; + for (;;) { + uint32_t variantMask; + if (afterDivision || gotValue) { + variantMask = VARIANT_NUMBER; + } else { + variantMask = aVariantMask | VARIANT_NUMBER; + } + if (!ParseCalcTerm(*storage, variantMask)) + return false; + MOZ_ASSERT(variantMask != 0, + "ParseCalcTerm did not set variantMask appropriately"); + MOZ_ASSERT(!(variantMask & VARIANT_NUMBER) || + !(variantMask & ~int32_t(VARIANT_NUMBER)), + "ParseCalcTerm did not set variantMask appropriately"); + + if (variantMask & VARIANT_NUMBER) { + // Simplify the value immediately so we can check for division by + // zero. + ReduceNumberCalcOps ops; + float number = mozilla::css::ComputeCalc(*storage, ops); + if (number == 0.0 && afterDivision) + return false; + storage->SetFloatValue(number, eCSSUnit_Number); + } else { + gotValue = true; + + if (storage != &aValue) { + // Simplify any numbers in the Times_L position (which are + // not simplified by the check above). + MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1), + "unexpected relationship to current storage"); + nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0); + ReduceNumberCalcOps ops; + float number = mozilla::css::ComputeCalc(leftValue, ops); + leftValue.SetFloatValue(number, eCSSUnit_Number); + } + } + + bool hadWS = RequireWhitespace(); + if (!GetToken(false)) { + *aHadFinalWS = hadWS; + break; + } + nsCSSUnit unit; + if (mToken.IsSymbol('*')) { + unit = gotValue ? eCSSUnit_Calc_Times_R : eCSSUnit_Calc_Times_L; + afterDivision = false; + } else if (mToken.IsSymbol('/')) { + unit = eCSSUnit_Calc_Divided; + afterDivision = true; + } else { + UngetToken(); + *aHadFinalWS = hadWS; + break; + } + + RefPtr arr = nsCSSValue::Array::Create(2); + arr->Item(0) = aValue; + storage = &arr->Item(1); + aValue.SetArrayValue(arr, unit); + } + + // Adjust aVariantMask (see comments above function) to reflect which + // option we took. + if (aVariantMask & VARIANT_NUMBER) { + if (gotValue) { + aVariantMask &= ~int32_t(VARIANT_NUMBER); + } else { + aVariantMask = VARIANT_NUMBER; + } + } else { + if (!gotValue) { + // We had to find a value, but we didn't. + return false; + } + } + + return true; +} + +// * If aVariantMask is VARIANT_NUMBER, this function parses the +// production. +// * If aVariantMask does not contain VARIANT_NUMBER, this function +// parses the production. +// * Otherwise (VARIANT_NUMBER and other bits) this function parses +// whichever one of the productions matches ***and modifies +// aVariantMask*** to reflect which one it has parsed by either +// removing VARIANT_NUMBER or removing all other bits. +bool +CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, uint32_t& aVariantMask) +{ + MOZ_ASSERT(aVariantMask != 0, "unexpected variant mask"); + if (!GetToken(true)) + return false; + // Either an additive expression in parentheses... + if (mToken.IsSymbol('(') || + // Treat nested calc() as plain parenthesis. + IsCSSTokenCalcFunction(mToken)) { + if (!ParseCalcAdditiveExpression(aValue, aVariantMask) || + !ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + return true; + } + // ... or just a value + UngetToken(); + // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero + // always gets picked up + if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) != + CSSParseResult::Ok) { + return false; + } + // ...and do the VARIANT_NUMBER check ourselves. + if (!(aVariantMask & VARIANT_NUMBER) && aValue.GetUnit() == eCSSUnit_Number) { + return false; + } + // If we did the value parsing, we need to adjust aVariantMask to + // reflect which option we took (see above). + if (aVariantMask & VARIANT_NUMBER) { + if (aValue.GetUnit() == eCSSUnit_Number) { + aVariantMask = VARIANT_NUMBER; + } else { + aVariantMask &= ~int32_t(VARIANT_NUMBER); + } + } + return true; +} + +// This function consumes all consecutive whitespace and returns whether +// there was any. +bool +CSSParserImpl::RequireWhitespace() +{ + if (!GetToken(false)) + return false; + if (mToken.mType != eCSSToken_Whitespace) { + UngetToken(); + return false; + } + // Skip any additional whitespace tokens. + if (GetToken(true)) { + UngetToken(); + } + return true; +} + +bool +CSSParserImpl::ParseRect(nsCSSPropertyID aPropID) +{ + nsCSSValue val; + if (ParseSingleTokenVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) { + AppendValue(aPropID, val); + return true; + } + + if (! GetToken(true)) { + return false; + } + + if (mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("rect")) { + nsCSSRect& rect = val.SetRectValue(); + bool useCommas; + NS_FOR_CSS_SIDES(side) { + if (!ParseSingleTokenVariant(rect.*(nsCSSRect::sides[side]), + VARIANT_AL, nullptr)) { + return false; + } + if (side == 0) { + useCommas = ExpectSymbol(',', true); + } else if (useCommas && side < 3) { + // Skip optional commas between elements, but only if the first + // separator was a comma. + if (!ExpectSymbol(',', true)) { + return false; + } + } + } + if (!ExpectSymbol(')', true)) { + return false; + } + } else { + UngetToken(); + return false; + } + + AppendValue(aPropID, val); + return true; +} + +bool +CSSParserImpl::ParseColumns() +{ + // We use a similar "fake value" hack to ParseListStyle, because + // "auto" is acceptable for both column-count and column-width. + // If the fake "auto" value is found, and one of the real values isn't, + // that means the fake auto value is meant for the real value we didn't + // find. + static const nsCSSPropertyID columnIDs[] = { + eCSSPropertyExtra_x_auto_value, + eCSSProperty_column_count, + eCSSProperty_column_width + }; + const int32_t numProps = MOZ_ARRAY_LENGTH(columnIDs); + + nsCSSValue values[numProps]; + int32_t found = ParseChoice(values, columnIDs, numProps); + if (found < 1) { + return false; + } + if ((found & (1|2|4)) == (1|2|4) && + values[0].GetUnit() == eCSSUnit_Auto) { + // We filled all 3 values, which is invalid + return false; + } + + if ((found & 2) == 0) { + // Provide auto column-count + values[1].SetAutoValue(); + } + if ((found & 4) == 0) { + // Provide auto column-width + values[2].SetAutoValue(); + } + + // Start at index 1 to skip the fake auto value. + for (int32_t index = 1; index < numProps; index++) { + AppendValue(columnIDs[index], values[index]); + } + return true; +} + +#define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \ + VARIANT_KEYWORD) +bool +CSSParserImpl::ParseContent() +{ + // We need to divide the 'content' keywords into two classes for + // ParseVariant's sake, so we can't just use nsCSSProps::kContentKTable. + static const KTableEntry kContentListKWs[] = { + { eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE }, + { eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE }, + { eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE }, + { eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE }, + { eCSSKeyword_UNKNOWN, -1 } + }; + + static const KTableEntry kContentSolitaryKWs[] = { + { eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT }, + { eCSSKeyword_UNKNOWN, -1 } + }; + + // Verify that these two lists add up to the size of + // nsCSSProps::kContentKTable. + MOZ_ASSERT(nsCSSProps::kContentKTable[ + ArrayLength(kContentListKWs) + + ArrayLength(kContentSolitaryKWs) - 2].mKeyword == + eCSSKeyword_UNKNOWN && + nsCSSProps::kContentKTable[ + ArrayLength(kContentListKWs) + + ArrayLength(kContentSolitaryKWs) - 2].mValue == -1, + "content keyword tables out of sync"); + + nsCSSValue value; + // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must + // be alone + if (!ParseSingleTokenVariant(value, VARIANT_HMK | VARIANT_NONE, + kContentSolitaryKWs)) { + nsCSSValueList* cur = value.SetListValue(); + for (;;) { + if (ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs) != + CSSParseResult::Ok) { + return false; + } + if (CheckEndProperty()) { + break; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_content, value); + return true; +} + +bool +CSSParserImpl::ParseCounterData(nsCSSPropertyID aPropID) +{ + static const nsCSSKeyword kCounterDataKTable[] = { + eCSSKeyword_none, + eCSSKeyword_UNKNOWN + }; + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + if (!GetToken(true)) { + return false; + } + if (mToken.mType != eCSSToken_Ident) { + UngetToken(); + return false; + } + + nsCSSValuePairList *cur = value.SetPairListValue(); + for (;;) { + if (!ParseCustomIdent(cur->mXValue, mToken.mIdent, kCounterDataKTable)) { + return false; + } + if (!GetToken(true)) { + break; + } + if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) { + cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer); + } else { + UngetToken(); + } + if (!GetToken(true)) { + break; + } + if (mToken.mType != eCSSToken_Ident) { + UngetToken(); + break; + } + cur->mNext = new nsCSSValuePairList; + cur = cur->mNext; + } + } + AppendValue(aPropID, value); + return true; +} + +bool +CSSParserImpl::ParseCursor() +{ + nsCSSValue value; + // 'inherit', 'initial' and 'unset' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + nsCSSValueList* cur = value.SetListValue(); + for (;;) { + if (!ParseSingleTokenVariant(cur->mValue, VARIANT_UK, + nsCSSProps::kCursorKTable)) { + return false; + } + if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last + break; + } + + // We have a URL, so make a value array with three values. + RefPtr val = nsCSSValue::Array::Create(3); + val->Item(0) = cur->mValue; + + // Parse optional x and y position of cursor hotspot (css3-ui). + if (ParseSingleTokenVariant(val->Item(1), VARIANT_NUMBER, nullptr)) { + // If we have one number, we must have two. + if (!ParseSingleTokenVariant(val->Item(2), VARIANT_NUMBER, nullptr)) { + return false; + } + } + cur->mValue.SetArrayValue(val, eCSSUnit_Array); + + if (!ExpectSymbol(',', true)) { // url must not be last + return false; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_cursor, value); + return true; +} + + +bool +CSSParserImpl::ParseFont() +{ + nsCSSValue family; + if (ParseSingleTokenVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) { + if (eCSSUnit_Inherit == family.GetUnit() || + eCSSUnit_Initial == family.GetUnit() || + eCSSUnit_Unset == family.GetUnit()) { + AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); + AppendValue(eCSSProperty_font_family, family); + AppendValue(eCSSProperty_font_style, family); + AppendValue(eCSSProperty_font_weight, family); + AppendValue(eCSSProperty_font_size, family); + AppendValue(eCSSProperty_line_height, family); + AppendValue(eCSSProperty_font_stretch, family); + AppendValue(eCSSProperty_font_size_adjust, family); + AppendValue(eCSSProperty_font_feature_settings, family); + AppendValue(eCSSProperty_font_language_override, family); + AppendValue(eCSSProperty_font_kerning, family); + AppendValue(eCSSProperty_font_synthesis, family); + AppendValue(eCSSProperty_font_variant_alternates, family); + AppendValue(eCSSProperty_font_variant_caps, family); + AppendValue(eCSSProperty_font_variant_east_asian, family); + AppendValue(eCSSProperty_font_variant_ligatures, family); + AppendValue(eCSSProperty_font_variant_numeric, family); + AppendValue(eCSSProperty_font_variant_position, family); + } + else { + AppendValue(eCSSProperty__x_system_font, family); + nsCSSValue systemFont(eCSSUnit_System_Font); + AppendValue(eCSSProperty_font_family, systemFont); + AppendValue(eCSSProperty_font_style, systemFont); + AppendValue(eCSSProperty_font_weight, systemFont); + AppendValue(eCSSProperty_font_size, systemFont); + AppendValue(eCSSProperty_line_height, systemFont); + AppendValue(eCSSProperty_font_stretch, systemFont); + AppendValue(eCSSProperty_font_size_adjust, systemFont); + AppendValue(eCSSProperty_font_feature_settings, systemFont); + AppendValue(eCSSProperty_font_language_override, systemFont); + AppendValue(eCSSProperty_font_kerning, systemFont); + AppendValue(eCSSProperty_font_synthesis, systemFont); + AppendValue(eCSSProperty_font_variant_alternates, systemFont); + AppendValue(eCSSProperty_font_variant_caps, systemFont); + AppendValue(eCSSProperty_font_variant_east_asian, systemFont); + AppendValue(eCSSProperty_font_variant_ligatures, systemFont); + AppendValue(eCSSProperty_font_variant_numeric, systemFont); + AppendValue(eCSSProperty_font_variant_position, systemFont); + } + return true; + } + + // Get optional font-style, font-variant, font-weight, font-stretch + // (in any order) + + // Indexes into fontIDs[] and values[] arrays. + const int kFontStyleIndex = 0; + const int kFontVariantIndex = 1; + const int kFontWeightIndex = 2; + const int kFontStretchIndex = 3; + + // The order of the initializers here must match the order of the indexes + // defined above! + static const nsCSSPropertyID fontIDs[] = { + eCSSProperty_font_style, + eCSSProperty_font_variant_caps, + eCSSProperty_font_weight, + eCSSProperty_font_stretch + }; + + const int32_t numProps = MOZ_ARRAY_LENGTH(fontIDs); + nsCSSValue values[numProps]; + int32_t found = ParseChoice(values, fontIDs, numProps); + if (found < 0 || + eCSSUnit_Inherit == values[kFontStyleIndex].GetUnit() || + eCSSUnit_Initial == values[kFontStyleIndex].GetUnit() || + eCSSUnit_Unset == values[kFontStyleIndex].GetUnit()) { // illegal data + return false; + } + if ((found & (1 << kFontStyleIndex)) == 0) { + // Provide default font-style + values[kFontStyleIndex].SetIntValue(NS_FONT_STYLE_NORMAL, + eCSSUnit_Enumerated); + } + if ((found & (1 << kFontVariantIndex)) == 0) { + // Provide default font-variant + values[kFontVariantIndex].SetNormalValue(); + } else { + if (values[kFontVariantIndex].GetUnit() == eCSSUnit_Enumerated && + values[kFontVariantIndex].GetIntValue() != + NS_FONT_VARIANT_CAPS_SMALLCAPS) { + // only normal or small-caps is allowed in font shorthand + // this also assumes other values for font-variant-caps never overlap + // possible values for style or weight + return false; + } + } + if ((found & (1 << kFontWeightIndex)) == 0) { + // Provide default font-weight + values[kFontWeightIndex].SetIntValue(NS_FONT_WEIGHT_NORMAL, + eCSSUnit_Enumerated); + } + if ((found & (1 << kFontStretchIndex)) == 0) { + // Provide default font-stretch + values[kFontStretchIndex].SetIntValue(NS_FONT_STRETCH_NORMAL, + eCSSUnit_Enumerated); + } + + // Get mandatory font-size + nsCSSValue size; + if (!ParseSingleTokenNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP, + nsCSSProps::kFontSizeKTable)) { + return false; + } + + // Get optional "/" line-height + nsCSSValue lineHeight; + if (ExpectSymbol('/', true)) { + if (ParseNonNegativeVariant(lineHeight, + VARIANT_NUMBER | VARIANT_LP | + VARIANT_NORMAL | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { + return false; + } + } + else { + lineHeight.SetNormalValue(); + } + + // Get final mandatory font-family + nsAutoParseCompoundProperty compound(this); + if (ParseFamily(family)) { + if (eCSSUnit_Inherit != family.GetUnit() && + eCSSUnit_Initial != family.GetUnit() && + eCSSUnit_Unset != family.GetUnit()) { + AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); + AppendValue(eCSSProperty_font_family, family); + AppendValue(eCSSProperty_font_style, values[kFontStyleIndex]); + AppendValue(eCSSProperty_font_variant_caps, values[kFontVariantIndex]); + AppendValue(eCSSProperty_font_weight, values[kFontWeightIndex]); + AppendValue(eCSSProperty_font_size, size); + AppendValue(eCSSProperty_line_height, lineHeight); + AppendValue(eCSSProperty_font_stretch, values[kFontStretchIndex]); + AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None)); + AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_kerning, + nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated)); + AppendValue(eCSSProperty_font_synthesis, + nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE, + eCSSUnit_Enumerated)); + AppendValue(eCSSProperty_font_variant_alternates, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_east_asian, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_ligatures, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_numeric, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_position, + nsCSSValue(eCSSUnit_Normal)); + return true; + } + } + return false; +} + +bool +CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue) +{ + if (!ParseSingleTokenVariant(aValue, VARIANT_HK | VARIANT_NONE, + nsCSSProps::kFontSynthesisKTable)) { + return false; + } + + // first value 'none' ==> done + if (eCSSUnit_None == aValue.GetUnit() || + eCSSUnit_Initial == aValue.GetUnit() || + eCSSUnit_Inherit == aValue.GetUnit() || + eCSSUnit_Unset == aValue.GetUnit()) + { + return true; + } + + // look for a second value + int32_t intValue = aValue.GetIntValue(); + nsCSSValue nextValue; + + if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) { + int32_t nextIntValue = nextValue.GetIntValue(); + if (nextIntValue & intValue) { + return false; + } + aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated); + } + + return true; +} + +// font-variant-alternates allows for a combination of multiple +// simple enumerated values and functional values. Functional values have +// parameter lists with one or more idents which are later resolved +// based on values defined in @font-feature-value rules. +// +// font-variant-alternates: swash(flowing) historical-forms styleset(alt-g, alt-m); +// +// So for this the nsCSSValue is set to a pair value, with one +// value for a bitmask of both simple and functional property values +// and another value containing a ValuePairList with lists of idents +// for each functional property value. +// +// pairValue +// o intValue +// NS_FONT_VARIANT_ALTERNATES_SWASH | +// NS_FONT_VARIANT_ALTERNATES_STYLESET +// o valuePairList, each element with +// - intValue - indicates which alternate +// - string or valueList of strings +// +// Note: when only 'historical-forms' is specified, there are no +// functional values to store, in which case the valuePairList is a +// single element dummy list. In all other cases, the length of the +// list will match the number of functional values. + +#define MAX_ALLOWED_FEATURES 512 + +static uint16_t +MaxElementsForAlternateType(nsCSSKeyword keyword) +{ + uint16_t maxElems = 1; + if (keyword == eCSSKeyword_styleset || + keyword == eCSSKeyword_character_variant) { + maxElems = MAX_ALLOWED_FEATURES; + } + return maxElems; +} + +bool +CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature, + nsCSSValue& aValue) +{ + if (!GetToken(true)) { + return false; + } + + bool isIdent = (mToken.mType == eCSSToken_Ident); + if (mToken.mType != eCSSToken_Function && !isIdent) { + UngetToken(); + return false; + } + + // ident ==> simple enumerated prop val (e.g. historical-forms) + // function ==> e.g. swash(flowing) styleset(alt-g, alt-m) + + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (!(eCSSKeyword_UNKNOWN < keyword && + nsCSSProps::FindKeyword(keyword, + (isIdent ? + nsCSSProps::kFontVariantAlternatesKTable : + nsCSSProps::kFontVariantAlternatesFuncsKTable), + aWhichFeature))) + { + // failed, pop token + UngetToken(); + return false; + } + + if (isIdent) { + aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated); + return true; + } + + return ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, + 1, MaxElementsForAlternateType(keyword), aValue); +} + +bool +CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { + return true; + } + + // iterate through parameters + nsCSSValue listValue; + int32_t feature, featureFlags = 0; + + // if no functional values, this may be a list with a single, unused element + listValue.SetListValue(); + + nsCSSValueList* list = nullptr; + nsCSSValue value; + while (ParseSingleAlternate(feature, value)) { + + // check to make sure value not already set + if (feature == 0 || + feature & featureFlags) { + return false; + } + + featureFlags |= feature; + + // if function, need to add to the list of functions + if (value.GetUnit() == eCSSUnit_Function) { + if (!list) { + list = listValue.GetListValue(); + } else { + list->mNext = new nsCSSValueList; + list = list->mNext; + } + list->mValue = value; + } + } + + if (featureFlags == 0) { + // ParseSingleAlternate failed the first time through the loop. + return false; + } + + nsCSSValue featureValue; + featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated); + aValue.SetPairValue(featureValue, listValue); + + return true; +} + +bool +CSSParserImpl::MergeBitmaskValue(int32_t aNewValue, + const int32_t aMasks[], + int32_t& aMergedValue) +{ + // check to make sure value not already set + if (aNewValue & aMergedValue) { + return false; + } + + const int32_t *m = aMasks; + int32_t c = 0; + + while (*m != MASK_END_VALUE) { + if (*m & aNewValue) { + c = aMergedValue & *m; + break; + } + m++; + } + + if (c) { + return false; + } + + aMergedValue |= aNewValue; + return true; +} + +// aMasks - array of masks for mutually-exclusive property values, +// e.g. proportial-nums, tabular-nums + +bool +CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue, + const KTableEntry aKeywordTable[], + const int32_t aMasks[]) +{ + // Parse at least one keyword + if (!ParseEnum(aValue, aKeywordTable)) { + return false; + } + + // look for more values + nsCSSValue nextValue; + int32_t mergedValue = aValue.GetIntValue(); + + while (ParseEnum(nextValue, aKeywordTable)) + { + if (!MergeBitmaskValue(nextValue.GetIntValue(), aMasks, mergedValue)) { + return false; + } + } + + aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated); + + return true; +} + +static const int32_t maskEastAsian[] = { + NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK, + NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { + return true; + } + + NS_ASSERTION(maskEastAsian[ArrayLength(maskEastAsian) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable, + maskEastAsian); +} + +bool +CSSParserImpl::ParseContain(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + return true; + } + static const int32_t maskContain[] = { MASK_END_VALUE }; + if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable, maskContain)) { + return false; + } + if (aValue.GetIntValue() & NS_STYLE_CONTAIN_STRICT) { + if (aValue.GetIntValue() != NS_STYLE_CONTAIN_STRICT) { + // Disallow any other keywords in combination with 'strict'. + return false; + } + // Strict implies layout, style, and paint. + // However, for serialization purposes, we keep the strict bit around. + aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT | + NS_STYLE_CONTAIN_ALL_BITS, eCSSUnit_Enumerated); + } + return true; +} + +static const int32_t maskLigatures[] = { + NS_FONT_VARIANT_LIGATURES_COMMON_MASK, + NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK, + NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK, + NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, + VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, + nullptr)) { + return true; + } + + NS_ASSERTION(maskLigatures[ArrayLength(maskLigatures) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable, + maskLigatures); +} + +static const int32_t maskNumeric[] = { + NS_FONT_VARIANT_NUMERIC_FIGURE_MASK, + NS_FONT_VARIANT_NUMERIC_SPACING_MASK, + NS_FONT_VARIANT_NUMERIC_FRACTION_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { + return true; + } + + NS_ASSERTION(maskNumeric[ArrayLength(maskNumeric) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable, + maskNumeric); +} + +bool +CSSParserImpl::ParseFontVariant() +{ + // parse single values - normal/inherit/none + nsCSSValue value; + nsCSSValue normal(eCSSUnit_Normal); + + if (ParseSingleTokenVariant(value, + VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, + nullptr)) { + AppendValue(eCSSProperty_font_variant_ligatures, value); + if (eCSSUnit_None == value.GetUnit()) { + // 'none' applies the value 'normal' to all properties other + // than 'font-variant-ligatures' + value.SetNormalValue(); + } + AppendValue(eCSSProperty_font_variant_alternates, value); + AppendValue(eCSSProperty_font_variant_caps, value); + AppendValue(eCSSProperty_font_variant_east_asian, value); + AppendValue(eCSSProperty_font_variant_numeric, value); + AppendValue(eCSSProperty_font_variant_position, value); + return true; + } + + // set each of the individual subproperties + int32_t altFeatures = 0, capsFeatures = 0, eastAsianFeatures = 0, + ligFeatures = 0, numericFeatures = 0, posFeatures = 0; + nsCSSValue altListValue; + nsCSSValueList* altList = nullptr; + + // if no functional values, this may be a list with a single, unused element + altListValue.SetListValue(); + + bool foundValid = false; // found at least one proper value + while (GetToken(true)) { + // only an ident or a function at this point + bool isFunction = (mToken.mType == eCSSToken_Function); + if (mToken.mType != eCSSToken_Ident && !isFunction) { + UngetToken(); + break; + } + + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (keyword == eCSSKeyword_UNKNOWN) { + UngetToken(); + return false; + } + + int32_t feature; + + // function? ==> font-variant-alternates + if (isFunction) { + if (!nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantAlternatesFuncsKTable, + feature) || + (feature & altFeatures)) { + UngetToken(); + return false; + } + + altFeatures |= feature; + nsCSSValue funcValue; + if (!ParseFunction(keyword, nullptr, VARIANT_IDENTIFIER, 1, + MaxElementsForAlternateType(keyword), funcValue) || + funcValue.GetUnit() != eCSSUnit_Function) { + UngetToken(); + return false; + } + + if (!altList) { + altList = altListValue.GetListValue(); + } else { + altList->mNext = new nsCSSValueList; + altList = altList->mNext; + } + altList->mValue = funcValue; + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantCapsKTable, + feature)) { + if (capsFeatures != 0) { + // multiple values for font-variant-caps + UngetToken(); + return false; + } + capsFeatures = feature; + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantAlternatesKTable, + feature)) { + if (feature & altFeatures) { + // same value repeated + UngetToken(); + return false; + } + altFeatures |= feature; + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantEastAsianKTable, + feature)) { + if (!MergeBitmaskValue(feature, maskEastAsian, eastAsianFeatures)) { + // multiple mutually exclusive values + UngetToken(); + return false; + } + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantLigaturesKTable, + feature)) { + if (keyword == eCSSKeyword_none || + !MergeBitmaskValue(feature, maskLigatures, ligFeatures)) { + // none or multiple mutually exclusive values + UngetToken(); + return false; + } + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantNumericKTable, + feature)) { + if (!MergeBitmaskValue(feature, maskNumeric, numericFeatures)) { + // multiple mutually exclusive values + UngetToken(); + return false; + } + } else if (nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantPositionKTable, + feature)) { + if (posFeatures != 0) { + // multiple values for font-variant-caps + UngetToken(); + return false; + } + posFeatures = feature; + } else { + // bogus keyword, bail... + UngetToken(); + return false; + } + + foundValid = true; + } + + if (!foundValid) { + return false; + } + + if (altFeatures) { + nsCSSValue featureValue; + featureValue.SetIntValue(altFeatures, eCSSUnit_Enumerated); + value.SetPairValue(featureValue, altListValue); + AppendValue(eCSSProperty_font_variant_alternates, value); + } else { + AppendValue(eCSSProperty_font_variant_alternates, normal); + } + + if (capsFeatures) { + value.SetIntValue(capsFeatures, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_font_variant_caps, value); + } else { + AppendValue(eCSSProperty_font_variant_caps, normal); + } + + if (eastAsianFeatures) { + value.SetIntValue(eastAsianFeatures, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_font_variant_east_asian, value); + } else { + AppendValue(eCSSProperty_font_variant_east_asian, normal); + } + + if (ligFeatures) { + value.SetIntValue(ligFeatures, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_font_variant_ligatures, value); + } else { + AppendValue(eCSSProperty_font_variant_ligatures, normal); + } + + if (numericFeatures) { + value.SetIntValue(numericFeatures, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_font_variant_numeric, value); + } else { + AppendValue(eCSSProperty_font_variant_numeric, normal); + } + + if (posFeatures) { + value.SetIntValue(posFeatures, eCSSUnit_Enumerated); + AppendValue(eCSSProperty_font_variant_position, value); + } else { + AppendValue(eCSSProperty_font_variant_position, normal); + } + + return true; +} + +bool +CSSParserImpl::ParseFontWeight(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT, + nsCSSProps::kFontWeightKTable)) { + if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value + int32_t intValue = aValue.GetIntValue(); + if ((100 <= intValue) && + (intValue <= 900) && + (0 == (intValue % 100))) { + return true; + } else { + UngetToken(); + return false; + } + } + return true; + } + return false; +} + +bool +CSSParserImpl::ParseOneFamily(nsAString& aFamily, + bool& aOneKeyword, + bool& aQuoted) +{ + if (!GetToken(true)) + return false; + + nsCSSToken* tk = &mToken; + + aOneKeyword = false; + aQuoted = false; + if (eCSSToken_Ident == tk->mType) { + aOneKeyword = true; + aFamily.Append(tk->mIdent); + for (;;) { + if (!GetToken(false)) + break; + + if (eCSSToken_Ident == tk->mType) { + aOneKeyword = false; + // We had at least another keyword before. + // "If a sequence of identifiers is given as a font family name, + // the computed value is the name converted to a string by joining + // all the identifiers in the sequence by single spaces." + // -- CSS 2.1, section 15.3 + // Whitespace tokens do not actually matter, + // identifier tokens can be separated by comments. + aFamily.Append(char16_t(' ')); + aFamily.Append(tk->mIdent); + } else if (eCSSToken_Whitespace != tk->mType) { + UngetToken(); + break; + } + } + return true; + + } else if (eCSSToken_String == tk->mType) { + aQuoted = true; + aFamily.Append(tk->mIdent); // XXX What if it had escaped quotes? + return true; + + } else { + UngetToken(); + return false; + } +} + + +static bool +AppendGeneric(nsCSSKeyword aKeyword, FontFamilyList *aFamilyList) +{ + switch (aKeyword) { + case eCSSKeyword_serif: + aFamilyList->Append(FontFamilyName(eFamily_serif)); + return true; + case eCSSKeyword_sans_serif: + aFamilyList->Append(FontFamilyName(eFamily_sans_serif)); + return true; + case eCSSKeyword_monospace: + aFamilyList->Append(FontFamilyName(eFamily_monospace)); + return true; + case eCSSKeyword_cursive: + aFamilyList->Append(FontFamilyName(eFamily_cursive)); + return true; + case eCSSKeyword_fantasy: + aFamilyList->Append(FontFamilyName(eFamily_fantasy)); + return true; + case eCSSKeyword__moz_fixed: + aFamilyList->Append(FontFamilyName(eFamily_moz_fixed)); + return true; + default: + break; + } + + return false; +} + +bool +CSSParserImpl::ParseFamily(nsCSSValue& aValue) +{ + RefPtr familyList = + new css::FontFamilyListRefCnt(); + nsAutoString family; + bool single, quoted; + + // keywords only have meaning in the first position + if (!ParseOneFamily(family, single, quoted)) + return false; + + // check for keywords, but only when keywords appear by themselves + // i.e. not in compounds such as font-family: default blah; + bool foundGeneric = false; + if (single) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family); + switch (keyword) { + case eCSSKeyword_inherit: + aValue.SetInheritValue(); + return true; + case eCSSKeyword_default: + // 605231 - don't parse unquoted 'default' reserved keyword + return false; + case eCSSKeyword_initial: + aValue.SetInitialValue(); + return true; + case eCSSKeyword_unset: + if (nsLayoutUtils::UnsetValueEnabled()) { + aValue.SetUnsetValue(); + return true; + } + break; + case eCSSKeyword__moz_use_system_font: + if (!IsParsingCompoundProperty()) { + aValue.SetSystemFontValue(); + return true; + } + break; + default: + foundGeneric = AppendGeneric(keyword, familyList); + } + } + + if (!foundGeneric) { + familyList->Append( + FontFamilyName(family, (quoted ? eQuotedName : eUnquotedName))); + } + + for (;;) { + if (!ExpectSymbol(',', true)) + break; + + nsAutoString nextFamily; + if (!ParseOneFamily(nextFamily, single, quoted)) + return false; + + // at this point unquoted keywords are not allowed + // as font family names but can appear within names + foundGeneric = false; + if (single) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily); + switch (keyword) { + case eCSSKeyword_inherit: + case eCSSKeyword_initial: + case eCSSKeyword_default: + case eCSSKeyword__moz_use_system_font: + return false; + case eCSSKeyword_unset: + if (nsLayoutUtils::UnsetValueEnabled()) { + return false; + } + break; + default: + foundGeneric = AppendGeneric(keyword, familyList); + break; + } + } + + if (!foundGeneric) { + familyList->Append( + FontFamilyName(nextFamily, (quoted ? eQuotedName : eUnquotedName))); + } + } + + if (familyList->IsEmpty()) { + return false; + } + + aValue.SetFontFamilyListValue(familyList); + return true; +} + +// src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )* +// uri-src: uri [ 'format(' string ( ',' string )* ')' ] +// local-src: 'local(' ( string | ident ) ')' + +bool +CSSParserImpl::ParseFontSrc(nsCSSValue& aValue) +{ + // could we maybe turn nsCSSValue::Array into InfallibleTArray? + InfallibleTArray values; + nsCSSValue cur; + for (;;) { + if (!GetToken(true)) + break; + + if (mToken.mType == eCSSToken_URL) { + SetValueToURL(cur, mToken.mIdent); + values.AppendElement(cur); + if (!ParseFontSrcFormat(values)) + return false; + + } else if (mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("local")) { + // css3-fonts does not specify a formal grammar for local(). + // The text permits both unquoted identifiers and quoted + // strings. We resolve this ambiguity in the spec by + // assuming that the appropriate production is a single + // , possibly surrounded by whitespace. + + nsAutoString family; + bool single, quoted; + if (!ParseOneFamily(family, single, quoted)) { + SkipUntil(')'); + return false; + } + if (!ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + + // reject generics + if (single) { + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(family); + switch (keyword) { + case eCSSKeyword_serif: + case eCSSKeyword_sans_serif: + case eCSSKeyword_monospace: + case eCSSKeyword_cursive: + case eCSSKeyword_fantasy: + case eCSSKeyword__moz_fixed: + return false; + default: + break; + } + } + + cur.SetStringValue(family, eCSSUnit_Local_Font); + values.AppendElement(cur); + } else { + // We don't know what to do with this token; unget it and error out + UngetToken(); + return false; + } + + if (!ExpectSymbol(',', true)) + break; + } + + if (values.Length() == 0) + return false; + + RefPtr srcVals + = nsCSSValue::Array::Create(values.Length()); + + uint32_t i; + for (i = 0; i < values.Length(); i++) + srcVals->Item(i) = values[i]; + aValue.SetArrayValue(srcVals, eCSSUnit_Array); + return true; +} + +bool +CSSParserImpl::ParseFontSrcFormat(InfallibleTArray & values) +{ + if (!GetToken(true)) + return true; // EOF harmless here + if (mToken.mType != eCSSToken_Function || + !mToken.mIdent.LowerCaseEqualsLiteral("format")) { + UngetToken(); + return true; + } + + do { + if (!GetToken(true)) + return false; // EOF - no need for SkipUntil + + if (mToken.mType != eCSSToken_String) { + UngetToken(); + SkipUntil(')'); + return false; + } + + nsCSSValue cur(mToken.mIdent, eCSSUnit_Font_Format); + values.AppendElement(cur); + } while (ExpectSymbol(',', true)); + + if (!ExpectSymbol(')', true)) { + SkipUntil(')'); + return false; + } + + return true; +} + +// font-ranges: urange ( ',' urange )* +bool +CSSParserImpl::ParseFontRanges(nsCSSValue& aValue) +{ + InfallibleTArray ranges; + for (;;) { + if (!GetToken(true)) + break; + + if (mToken.mType != eCSSToken_URange) { + UngetToken(); + break; + } + + // An invalid range token is a parsing error, causing the entire + // descriptor to be ignored. + if (!mToken.mIntegerValid) + return false; + + uint32_t low = mToken.mInteger; + uint32_t high = mToken.mInteger2; + + // A range that descends, or a range that is entirely outside the + // current range of Unicode (U+0-10FFFF) is ignored, but does not + // invalidate the descriptor. A range that straddles the high end + // is clipped. + if (low <= 0x10FFFF && low <= high) { + if (high > 0x10FFFF) + high = 0x10FFFF; + + ranges.AppendElement(low); + ranges.AppendElement(high); + } + if (!ExpectSymbol(',', true)) + break; + } + + if (ranges.Length() == 0) + return false; + + RefPtr srcVals + = nsCSSValue::Array::Create(ranges.Length()); + + for (uint32_t i = 0; i < ranges.Length(); i++) + srcVals->Item(i).SetIntValue(ranges[i], eCSSUnit_Integer); + aValue.SetArrayValue(srcVals, eCSSUnit_Array); + return true; +} + +// font-feature-settings: normal | [, ]* +// = [ | on | off ]? + +// minimum - "tagx", "tagy", "tagz" +// edge error case - "tagx" on 1, "tagx" "tagy", "tagx" -1, "tagx" big + +// pair value is always x = string, y = int + +// font feature tags must be four ASCII characters +#define FEATURE_TAG_LENGTH 4 + +static bool +ValidFontFeatureTag(const nsString& aTag) +{ + if (aTag.Length() != FEATURE_TAG_LENGTH) { + return false; + } + uint32_t i; + for (i = 0; i < FEATURE_TAG_LENGTH; i++) { + uint32_t ch = aTag[i]; + if (ch < 0x20 || ch > 0x7e) { + return false; + } + } + return true; +} + +bool +CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { + return true; + } + + nsCSSValuePairList *cur = aValue.SetPairListValue(); + for (;;) { + // feature tag + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_String || + !ValidFontFeatureTag(mToken.mIdent)) { + UngetToken(); + return false; + } + cur->mXValue.SetStringValue(mToken.mIdent, eCSSUnit_String); + + if (!GetToken(true)) { + cur->mYValue.SetIntValue(1, eCSSUnit_Integer); + break; + } + + // optional value or on/off keyword + if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid && + mToken.mInteger >= 0) { + cur->mYValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer); + } else if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("on")) { + cur->mYValue.SetIntValue(1, eCSSUnit_Integer); + } else if (mToken.mType == eCSSToken_Ident && + mToken.mIdent.LowerCaseEqualsLiteral("off")) { + cur->mYValue.SetIntValue(0, eCSSUnit_Integer); + } else { + // something other than value/on/off, set default value + cur->mYValue.SetIntValue(1, eCSSUnit_Integer); + UngetToken(); + } + + if (!ExpectSymbol(',', true)) { + break; + } + + cur->mNext = new nsCSSValuePairList; + cur = cur->mNext; + } + + return true; +} + +bool +CSSParserImpl::ParseListStyle() +{ + // 'list-style' can accept 'none' for two different subproperties, + // 'list-style-type' and 'list-style-image'. In order to accept + // 'none' as the value of either but still allow another value for + // either, we need to ensure that the first 'none' we find gets + // allocated to a dummy property instead. Since parse function for + // 'list-style-type' could accept values for 'list-style-position', + // we put position in front of type. + static const nsCSSPropertyID listStyleIDs[] = { + eCSSPropertyExtra_x_none_value, + eCSSProperty_list_style_position, + eCSSProperty_list_style_type, + eCSSProperty_list_style_image + }; + + nsCSSValue values[MOZ_ARRAY_LENGTH(listStyleIDs)]; + int32_t found = + ParseChoice(values, listStyleIDs, ArrayLength(listStyleIDs)); + if (found < 1) { + return false; + } + + if ((found & (1|4|8)) == (1|4|8)) { + if (values[0].GetUnit() == eCSSUnit_None) { + // We found a 'none' plus another value for both of + // 'list-style-type' and 'list-style-image'. This is a parse + // error, since the 'none' has to count for at least one of them. + return false; + } else { + NS_ASSERTION(found == (1|2|4|8) && values[0] == values[1] && + values[0] == values[2] && values[0] == values[3], + "should be a special value"); + } + } + + if ((found & 2) == 0) { + values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, + eCSSUnit_Enumerated); + } + if ((found & 4) == 0) { + // Provide default values + nsString type = (found & 1) ? + NS_LITERAL_STRING("none") : NS_LITERAL_STRING("disc"); + values[2].SetStringValue(type, eCSSUnit_Ident); + } + if ((found & 8) == 0) { + values[3].SetNoneValue(); + } + + // Start at 1 to avoid appending fake value. + for (uint32_t index = 1; index < ArrayLength(listStyleIDs); ++index) { + AppendValue(listStyleIDs[index], values[index]); + } + return true; +} + +bool +CSSParserImpl::ParseListStyleType(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_STRING, + nullptr)) { + return true; + } + + if (ParseCounterStyleNameValue(aValue) || ParseSymbols(aValue)) { + return true; + } + + return false; +} + +bool +CSSParserImpl::ParseMargin() +{ + static const nsCSSPropertyID kMarginSideIDs[] = { + eCSSProperty_margin_top, + eCSSProperty_margin_right, + eCSSProperty_margin_bottom, + eCSSProperty_margin_left + }; + + return ParseBoxProperties(kMarginSideIDs); +} + +bool +CSSParserImpl::ParseObjectPosition() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) && + !ParsePositionValue(value)) { + return false; + } + AppendValue(eCSSProperty_object_position, value); + return true; +} + +bool +CSSParserImpl::ParseOutline() +{ + const int32_t numProps = 3; + static const nsCSSPropertyID kOutlineIDs[] = { + eCSSProperty_outline_color, + eCSSProperty_outline_style, + eCSSProperty_outline_width + }; + + nsCSSValue values[numProps]; + int32_t found = ParseChoice(values, kOutlineIDs, numProps); + if (found < 1) { + return false; + } + + // Provide default values + if ((found & 1) == 0) { // Provide default outline-color + values[0].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + if ((found & 2) == 0) { // Provide default outline-style + values[1].SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated); + } + if ((found & 4) == 0) { // Provide default outline-width + values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated); + } + + int32_t index; + for (index = 0; index < numProps; index++) { + AppendValue(kOutlineIDs[index], values[index]); + } + return true; +} + +bool +CSSParserImpl::ParseOverflow() +{ + nsCSSValue overflow; + if (!ParseSingleTokenVariant(overflow, VARIANT_HK, + nsCSSProps::kOverflowKTable)) { + return false; + } + + nsCSSValue overflowX(overflow); + nsCSSValue overflowY(overflow); + if (eCSSUnit_Enumerated == overflow.GetUnit()) + switch(overflow.GetIntValue()) { + case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL: + overflowX.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated); + overflowY.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated); + break; + case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL: + overflowX.SetIntValue(NS_STYLE_OVERFLOW_HIDDEN, eCSSUnit_Enumerated); + overflowY.SetIntValue(NS_STYLE_OVERFLOW_SCROLL, eCSSUnit_Enumerated); + break; + } + AppendValue(eCSSProperty_overflow_x, overflowX); + AppendValue(eCSSProperty_overflow_y, overflowY); + return true; +} + +bool +CSSParserImpl::ParsePadding() +{ + static const nsCSSPropertyID kPaddingSideIDs[] = { + eCSSProperty_padding_top, + eCSSProperty_padding_right, + eCSSProperty_padding_bottom, + eCSSProperty_padding_left + }; + + return ParseBoxProperties(kPaddingSideIDs); +} + +bool +CSSParserImpl::ParseQuotes() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_HOS, nullptr)) { + return false; + } + if (value.GetUnit() == eCSSUnit_String) { + nsCSSValue open = value; + nsCSSValuePairList* quotes = value.SetPairListValue(); + for (;;) { + quotes->mXValue = open; + // get mandatory close + if (!ParseSingleTokenVariant(quotes->mYValue, VARIANT_STRING, nullptr)) { + return false; + } + // look for another open + if (!ParseSingleTokenVariant(open, VARIANT_STRING, nullptr)) { + break; + } + quotes->mNext = new nsCSSValuePairList; + quotes = quotes->mNext; + } + } + AppendValue(eCSSProperty_quotes, value); + return true; +} + +bool +CSSParserImpl::ParseTextDecoration() +{ + static const nsCSSPropertyID kTextDecorationIDs[] = { + eCSSProperty_text_decoration_line, + eCSSProperty_text_decoration_style, + eCSSProperty_text_decoration_color + }; + const int32_t numProps = MOZ_ARRAY_LENGTH(kTextDecorationIDs); + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, kTextDecorationIDs, numProps); + if (found < 1) { + return false; + } + + // Provide default values + if ((found & 1) == 0) { // Provide default text-decoration-line + values[0].SetIntValue(NS_STYLE_TEXT_DECORATION_LINE_NONE, + eCSSUnit_Enumerated); + } + if ((found & 2) == 0) { // Provide default text-decoration-style + values[1].SetIntValue(NS_STYLE_TEXT_DECORATION_STYLE_SOLID, + eCSSUnit_Enumerated); + } + if ((found & 4) == 0) { // Provide default text-decoration-color + values[2].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + + for (int32_t index = 0; index < numProps; index++) { + AppendValue(kTextDecorationIDs[index], values[index]); + } + return true; +} + +bool +CSSParserImpl::ParseTextEmphasis() +{ + static constexpr nsCSSPropertyID kTextEmphasisIDs[] = { + eCSSProperty_text_emphasis_style, + eCSSProperty_text_emphasis_color + }; + constexpr int32_t numProps = MOZ_ARRAY_LENGTH(kTextEmphasisIDs); + nsCSSValue values[numProps]; + + int32_t found = ParseChoice(values, kTextEmphasisIDs, numProps); + if (found < 1) { + return false; + } + + if (!(found & 1)) { // Provide default text-emphasis-style + values[0].SetNoneValue(); + } + if (!(found & 2)) { // Provide default text-emphasis-color + values[1].SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor); + } + + for (int32_t index = 0; index < numProps; index++) { + AppendValue(kTextEmphasisIDs[index], values[index]); + } + return true; +} + +bool +CSSParserImpl::ParseTextEmphasisPosition(nsCSSValue& aValue) +{ + static_assert((NS_STYLE_TEXT_EMPHASIS_POSITION_OVER ^ + NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER ^ + NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT ^ + NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT) == + (NS_STYLE_TEXT_EMPHASIS_POSITION_OVER | + NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER | + NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT | + NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT), + "text-emphasis-position constants should be bitmasks"); + + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { + return true; + } + + nsCSSValue first, second; + const auto& kTable = nsCSSProps::kTextEmphasisPositionKTable; + if (!ParseSingleTokenVariant(first, VARIANT_KEYWORD, kTable) || + !ParseSingleTokenVariant(second, VARIANT_KEYWORD, kTable)) { + return false; + } + + auto firstValue = first.GetIntValue(); + auto secondValue = second.GetIntValue(); + if ((firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER || + firstValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER) == + (secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_OVER || + secondValue == NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)) { + return false; + } + + aValue.SetIntValue(firstValue | secondValue, eCSSUnit_Enumerated); + return true; +} + +bool +CSSParserImpl::ParseTextEmphasisStyle(nsCSSValue& aValue) +{ + static_assert((NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK ^ + NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) == + (NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK | + NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK), + "text-emphasis-style shape and fill constants " + "should not intersect"); + static_assert(NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED == 0, + "Making 'filled' zero ensures that if neither 'filled' nor " + "'open' is specified, we compute it to 'filled' per spec"); + + if (ParseSingleTokenVariant(aValue, VARIANT_HOS, nullptr)) { + return true; + } + + nsCSSValue first, second; + const auto& fillKTable = nsCSSProps::kTextEmphasisStyleFillKTable; + const auto& shapeKTable = nsCSSProps::kTextEmphasisStyleShapeKTable; + if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, fillKTable)) { + ParseSingleTokenVariant(second, VARIANT_KEYWORD, shapeKTable); + } else if (ParseSingleTokenVariant(first, VARIANT_KEYWORD, shapeKTable)) { + ParseSingleTokenVariant(second, VARIANT_KEYWORD, fillKTable); + } else { + return false; + } + + auto value = first.GetIntValue(); + if (second.GetUnit() == eCSSUnit_Enumerated) { + value |= second.GetIntValue(); + } + aValue.SetIntValue(value, eCSSUnit_Enumerated); + return true; +} + +bool +CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableEntry aTable[]) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { + // 'inherit', 'initial' and 'unset' must be alone + return true; + } + + nsCSSValue left; + if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD, aTable)) { + return false; + } + + if (!nsLayoutUtils::IsTextAlignUnsafeValueEnabled()) { + aValue = left; + return true; + } + + nsCSSValue right; + if (ParseSingleTokenVariant(right, VARIANT_KEYWORD, aTable)) { + // 'true' must be combined with some other value than 'true'. + if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE && + right.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) { + return false; + } + aValue.SetPairValue(left, right); + } else { + // Single value 'true' is not allowed. + if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) { + return false; + } + aValue = left; + } + return true; +} + +bool +CSSParserImpl::ParseTextAlign(nsCSSValue& aValue) +{ + return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable); +} + +bool +CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue) +{ + return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable); +} + +bool +CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue) +{ + static_assert((NS_STYLE_TEXT_DECORATION_LINE_NONE ^ + NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE ^ + NS_STYLE_TEXT_DECORATION_LINE_OVERLINE ^ + NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH ^ + NS_STYLE_TEXT_DECORATION_LINE_BLINK ^ + NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) == + (NS_STYLE_TEXT_DECORATION_LINE_NONE | + NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE | + NS_STYLE_TEXT_DECORATION_LINE_OVERLINE | + NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH | + NS_STYLE_TEXT_DECORATION_LINE_BLINK | + NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS), + "text decoration constants need to be bitmasks"); + if (ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTextDecorationLineKTable)) { + if (eCSSUnit_Enumerated == aValue.GetUnit()) { + int32_t intValue = aValue.GetIntValue(); + if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) { + // look for more keywords + nsCSSValue keyword; + int32_t index; + for (index = 0; index < 3; index++) { + if (ParseEnum(keyword, nsCSSProps::kTextDecorationLineKTable)) { + int32_t newValue = keyword.GetIntValue(); + if (newValue == NS_STYLE_TEXT_DECORATION_LINE_NONE || + newValue & intValue) { + // 'none' keyword in conjuction with others is not allowed, and + // duplicate keyword is not allowed. + return false; + } + intValue |= newValue; + } + else { + break; + } + } + aValue.SetIntValue(intValue, eCSSUnit_Enumerated); + } + } + return true; + } + return false; +} + +bool +CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { + // 'inherit', 'initial' and 'unset' must be alone + return true; + } + + nsCSSValue left; + if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD | VARIANT_STRING, + nsCSSProps::kTextOverflowKTable)) + return false; + + nsCSSValue right; + if (ParseSingleTokenVariant(right, VARIANT_KEYWORD | VARIANT_STRING, + nsCSSProps::kTextOverflowKTable)) + aValue.SetPairValue(left, right); + else { + aValue = left; + } + return true; +} + +bool +CSSParserImpl::ParseTouchAction(nsCSSValue& aValue) +{ + // Avaliable values of property touch-action: + // auto | none | [pan-x || pan-y] | manipulation + + if (!ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTouchActionKTable)) { + return false; + } + + // Auto and None keywords aren't allowed in conjunction with others. + // Also inherit, initial and unset values are available. + if (eCSSUnit_Enumerated != aValue.GetUnit()) { + return true; + } + + int32_t intValue = aValue.GetIntValue(); + nsCSSValue nextValue; + if (ParseEnum(nextValue, nsCSSProps::kTouchActionKTable)) { + int32_t nextIntValue = nextValue.GetIntValue(); + + // duplicates aren't allowed. + if (nextIntValue & intValue) { + return false; + } + + // Auto and None and Manipulation is not allowed in conjunction with others. + if ((intValue | nextIntValue) & (NS_STYLE_TOUCH_ACTION_NONE | + NS_STYLE_TOUCH_ACTION_AUTO | + NS_STYLE_TOUCH_ACTION_MANIPULATION)) { + return false; + } + + aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated); + } + + return true; +} + +bool +CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue) +{ + if (!ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTextCombineUprightKTable)) { + return false; + } + + // if 'digits', need to check for an explicit number [2, 3, 4] + if (eCSSUnit_Enumerated == aValue.GetUnit() && + aValue.GetIntValue() == NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) { + if (!nsLayoutUtils::TextCombineUprightDigitsEnabled()) { + return false; + } + if (!GetToken(true)) { + return true; + } + if (mToken.mType == eCSSToken_Number && mToken.mIntegerValid) { + switch (mToken.mInteger) { + case 2: // already set, nothing to do + break; + case 3: + aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3, + eCSSUnit_Enumerated); + break; + case 4: + aValue.SetIntValue(NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_4, + eCSSUnit_Enumerated); + break; + default: + // invalid digits value + return false; + } + } else { + UngetToken(); + } + } + return true; +} + +/////////////////////////////////////////////////////// +// transform Parsing Implementation + +/* Reads a function list of arguments and consumes the closing parenthesis. + * Do not call this function directly; it's meant to be called from + * ParseFunction. + */ +bool +CSSParserImpl::ParseFunctionInternals(const uint32_t aVariantMask[], + uint32_t aVariantMaskAll, + uint16_t aMinElems, + uint16_t aMaxElems, + InfallibleTArray &aOutput) +{ + NS_ASSERTION((aVariantMask && !aVariantMaskAll) || + (!aVariantMask && aVariantMaskAll), + "only one of the two variant mask parameters can be set"); + + for (uint16_t index = 0; index < aMaxElems; ++index) { + nsCSSValue newValue; + uint32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; + if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) { + break; + } + + if (nsCSSValue::IsFloatUnit(newValue.GetUnit())) { + // Clamp infinity or -infinity values to max float or -max float to avoid + // calculations with infinity. + newValue.SetFloatValue( + mozilla::clamped(newValue.GetFloatValue(), + -std::numeric_limits::max(), + std::numeric_limits::max()), + newValue.GetUnit()); + } + + aOutput.AppendElement(newValue); + + if (ExpectSymbol(',', true)) { + // Move on to the next argument if we see a comma. + continue; + } + + if (ExpectSymbol(')', true)) { + // Make sure we've read enough symbols if we see a closing parenthesis. + return (index + 1) >= aMinElems; + } + + // Only a comma or a closing parenthesis is valid after an argument. + break; + } + + // If we're here, we've hit an error without seeing a closing parenthesis or + // we've read too many elements without seeing a closing parenthesis. + SkipUntil(')'); + return false; +} + +/* Parses a function [ input of the form (a [, b]*) ] and stores it + * as an nsCSSValue that holds a function of the form + * function-name arg1 arg2 ... argN + * + * On error, the return value is false. + * + * @param aFunction The name of the function that we're reading. + * @param aAllowedTypes An array of values corresponding to the legal + * types for each element in the function. The zeroth element in the + * array corresponds to the first function parameter, etc. The length + * of this array _must_ be greater than or equal to aMaxElems or the + * behavior is undefined. If not null, aAllowTypesAll must be 0. + * @param aAllowedTypesAll If set, every element tested for these types + * @param aMinElems Minimum number of elements to read. Reading fewer than + * this many elements will result in the function failing. + * @param aMaxElems Maximum number of elements to read. Reading more than + * this many elements will result in the function failing. + * @param aValue (out) The value that was parsed. + */ +bool +CSSParserImpl::ParseFunction(nsCSSKeyword aFunction, + const uint32_t aAllowedTypes[], + uint32_t aAllowedTypesAll, + uint16_t aMinElems, uint16_t aMaxElems, + nsCSSValue &aValue) +{ + NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) || + (!aAllowedTypes && aAllowedTypesAll), + "only one of the two allowed type parameter can be set"); + typedef InfallibleTArray::size_type arrlen_t; + + /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1 + * elements stored in the the nsCSSValue::Array. + */ + static const arrlen_t MAX_ALLOWED_ELEMS = 0xFFFE; + + /* Read in a list of values as an array, failing if we can't or if + * it's out of bounds. + * + * We reserve 16 entries in the foundValues array in order to avoid + * having to resize the array dynamically when parsing some well-formed + * functions. The number 16 is coming from the number of arguments that + * matrix3d() accepts. + */ + AutoTArray foundValues; + if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems, + aMaxElems, foundValues)) { + return false; + } + + /* + * In case the user has given us more than 2^16 - 2 arguments, + * we'll truncate them at 2^16 - 2 arguments. + */ + uint16_t numArgs = std::min(foundValues.Length(), MAX_ALLOWED_ELEMS); + RefPtr convertedArray = + aValue.InitFunction(aFunction, numArgs); + + /* Copy things over. */ + for (uint16_t index = 0; index < numArgs; ++index) + convertedArray->Item(index + 1) = foundValues[static_cast(index)]; + + /* Return it! */ + return true; +} + +/** + * Given a token, determines the minimum and maximum number of function + * parameters to read, along with the mask that should be used to read + * those function parameters. If the token isn't a transform function, + * returns an error. + * + * @param aToken The token identifying the function. + * @param aIsPrefixed If true, parse matrices using the matrix syntax + * for -moz-transform. + * @param aDisallowRelativeValues If true, only allow variants that are + * numbers or have non-relative dimensions. + * @param aMinElems [out] The minimum number of elements to read. + * @param aMaxElems [out] The maximum number of elements to read + * @param aVariantMask [out] The variant mask to use during parsing + * @return Whether the information was loaded successfully. + */ +static bool GetFunctionParseInformation(nsCSSKeyword aToken, + bool aIsPrefixed, + bool aDisallowRelativeValues, + uint16_t &aMinElems, + uint16_t &aMaxElems, + const uint32_t *& aVariantMask) +{ +/* These types represent the common variant masks that will be used to + * parse out the individual functions. The order in the enumeration + * must match the order in which the masks are declared. + */ + enum { eLengthPercentCalc, + eLengthCalc, + eAbsoluteLengthCalc, + eTwoLengthPercentCalcs, + eTwoAbsoluteLengthCalcs, + eTwoLengthPercentCalcsOneLengthCalc, + eThreeAbsoluteLengthCalc, + eAngle, + eTwoAngles, + eNumber, + eNonNegativeLength, + eNonNegativeAbsoluteLength, + eTwoNumbers, + eThreeNumbers, + eThreeNumbersOneAngle, + eMatrix, + eMatrixPrefixed, + eMatrix3d, + eMatrix3dPrefixed, + eNumVariantMasks }; + static const int32_t kMaxElemsPerFunction = 16; + static const uint32_t kVariantMasks[eNumVariantMasks][kMaxElemsPerFunction] = { + {VARIANT_LPCALC}, + {VARIANT_LCALC}, + {VARIANT_LB}, + {VARIANT_LPCALC, VARIANT_LPCALC}, + {VARIANT_LBCALC, VARIANT_LBCALC}, + {VARIANT_LPCALC, VARIANT_LPCALC, VARIANT_LCALC}, + {VARIANT_LBCALC, VARIANT_LBCALC, VARIANT_LBCALC}, + {VARIANT_ANGLE_OR_ZERO}, + {VARIANT_ANGLE_OR_ZERO, VARIANT_ANGLE_OR_ZERO}, + {VARIANT_NUMBER}, + {VARIANT_LENGTH|VARIANT_NONNEGATIVE_DIMENSION}, + {VARIANT_LB|VARIANT_NONNEGATIVE_DIMENSION}, + {VARIANT_NUMBER, VARIANT_NUMBER}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_ANGLE_OR_ZERO}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_LPNCALC, VARIANT_LPNCALC}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER}, + {VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, VARIANT_NUMBER, + VARIANT_LPNCALC, VARIANT_LPNCALC, VARIANT_LNCALC, VARIANT_NUMBER}}; + // Map from a mask to a congruent mask that excludes relative variants. + static const int32_t kNonRelativeVariantMap[eNumVariantMasks] = { + eAbsoluteLengthCalc, + eAbsoluteLengthCalc, + eAbsoluteLengthCalc, + eTwoAbsoluteLengthCalcs, + eTwoAbsoluteLengthCalcs, + eThreeAbsoluteLengthCalc, + eThreeAbsoluteLengthCalc, + eAngle, + eTwoAngles, + eNumber, + eNonNegativeAbsoluteLength, + eNonNegativeAbsoluteLength, + eTwoNumbers, + eThreeNumbers, + eThreeNumbersOneAngle, + eMatrix, + eMatrix, + eMatrix3d, + eMatrix3d }; + +#ifdef DEBUG + static const uint8_t kVariantMaskLengths[eNumVariantMasks] = + {1, 1, 1, 2, 2, 3, 3, 1, 2, 1, 1, 1, 2, 3, 4, 6, 6, 16, 16}; +#endif + + int32_t variantIndex = eNumVariantMasks; + + switch (aToken) { + case eCSSKeyword_translatex: + case eCSSKeyword_translatey: + /* Exactly one length or percent. */ + variantIndex = eLengthPercentCalc; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_translatez: + /* Exactly one length */ + variantIndex = eLengthCalc; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_translate3d: + /* Exactly two lengthds or percents and a number */ + variantIndex = eTwoLengthPercentCalcsOneLengthCalc; + aMinElems = 3U; + aMaxElems = 3U; + break; + case eCSSKeyword_scalez: + case eCSSKeyword_scalex: + case eCSSKeyword_scaley: + /* Exactly one scale factor. */ + variantIndex = eNumber; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_scale3d: + /* Exactly three scale factors. */ + variantIndex = eThreeNumbers; + aMinElems = 3U; + aMaxElems = 3U; + break; + case eCSSKeyword_rotatex: + case eCSSKeyword_rotatey: + case eCSSKeyword_rotate: + case eCSSKeyword_rotatez: + /* Exactly one angle. */ + variantIndex = eAngle; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_rotate3d: + variantIndex = eThreeNumbersOneAngle; + aMinElems = 4U; + aMaxElems = 4U; + break; + case eCSSKeyword_translate: + /* One or two lengths or percents. */ + variantIndex = eTwoLengthPercentCalcs; + aMinElems = 1U; + aMaxElems = 2U; + break; + case eCSSKeyword_skew: + /* Exactly one or two angles. */ + variantIndex = eTwoAngles; + aMinElems = 1U; + aMaxElems = 2U; + break; + case eCSSKeyword_scale: + /* One or two scale factors. */ + variantIndex = eTwoNumbers; + aMinElems = 1U; + aMaxElems = 2U; + break; + case eCSSKeyword_skewx: + /* Exactly one angle. */ + variantIndex = eAngle; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_skewy: + /* Exactly one angle. */ + variantIndex = eAngle; + aMinElems = 1U; + aMaxElems = 1U; + break; + case eCSSKeyword_matrix: + /* Six values, all numbers. */ + variantIndex = aIsPrefixed ? eMatrixPrefixed : eMatrix; + aMinElems = 6U; + aMaxElems = 6U; + break; + case eCSSKeyword_matrix3d: + /* 16 matrix values, all numbers */ + variantIndex = aIsPrefixed ? eMatrix3dPrefixed : eMatrix3d; + aMinElems = 16U; + aMaxElems = 16U; + break; + case eCSSKeyword_perspective: + /* Exactly one scale number. */ + variantIndex = eNonNegativeLength; + aMinElems = 1U; + aMaxElems = 1U; + break; + default: + /* Oh dear, we didn't match. Report an error. */ + return false; + } + + if (aDisallowRelativeValues) { + variantIndex = kNonRelativeVariantMap[variantIndex]; + } + + NS_ASSERTION(aMinElems > 0, "Didn't update minimum elements!"); + NS_ASSERTION(aMaxElems > 0, "Didn't update maximum elements!"); + NS_ASSERTION(aMinElems <= aMaxElems, "aMinElems > aMaxElems!"); + NS_ASSERTION(variantIndex >= 0, "Invalid variant mask!"); + NS_ASSERTION(variantIndex < eNumVariantMasks, "Invalid variant mask!"); +#ifdef DEBUG + NS_ASSERTION(aMaxElems <= kVariantMaskLengths[variantIndex], + "Invalid aMaxElems for this variant mask."); +#endif + + // Convert the index into a mask. + aVariantMask = kVariantMasks[variantIndex]; + + return true; +} + +bool CSSParserImpl::ParseWillChange() +{ + nsCSSValue listValue; + nsCSSValueList* currentListValue = listValue.SetListValue(); + bool first = true; + for (;;) { + const uint32_t variantMask = VARIANT_IDENTIFIER | + VARIANT_INHERIT | + VARIANT_NONE | + VARIANT_ALL | + VARIANT_AUTO; + nsCSSValue value; + if (!ParseSingleTokenVariant(value, variantMask, nullptr)) { + return false; + } + + if (value.GetUnit() == eCSSUnit_None || + value.GetUnit() == eCSSUnit_All) + { + return false; + } + + if (value.GetUnit() != eCSSUnit_Ident) { + if (first) { + AppendValue(eCSSProperty_will_change, value); + return true; + } else { + return false; + } + } + + nsString str; + value.GetStringValue(str); + if (str.LowerCaseEqualsLiteral("default") || + str.LowerCaseEqualsLiteral("will-change")) { + return false; + } + + currentListValue->mValue = value; + + if (!ExpectSymbol(',', true)) { + break; + } + currentListValue->mNext = new nsCSSValueList; + currentListValue = currentListValue->mNext; + first = false; + } + + AppendValue(eCSSProperty_will_change, listValue); + return true; +} + +/* Reads a single transform function from the tokenizer stream, reporting an + * error if something goes wrong. + */ +bool +CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, + bool aDisallowRelativeValues, + nsCSSValue& aValue) +{ + if (!GetToken(true)) + return false; + + if (mToken.mType != eCSSToken_Function) { + UngetToken(); + return false; + } + + const uint32_t* variantMask; + uint16_t minElems, maxElems; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + + if (!GetFunctionParseInformation(keyword, aIsPrefixed, + aDisallowRelativeValues, + minElems, maxElems, + variantMask)) + return false; + + return ParseFunction(keyword, variantMask, 0, minElems, maxElems, aValue); +} + +/* Parses a transform property list by continuously reading in properties + * and constructing a matrix from it. + */ +bool +CSSParserImpl::ParseTransform(bool aIsPrefixed, bool aDisallowRelativeValues) +{ + nsCSSValue value; + // 'inherit', 'initial', 'unset' and 'none' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + nsCSSValueSharedList* list = new nsCSSValueSharedList; + value.SetSharedListValue(list); + list->mHead = new nsCSSValueList; + nsCSSValueList* cur = list->mHead; + for (;;) { + if (!ParseSingleTransform(aIsPrefixed, aDisallowRelativeValues, + cur->mValue)) { + return false; + } + if (CheckEndProperty()) { + break; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_transform, value); + return true; +} + +/* Reads a polygon function's argument list. + */ +bool +CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue) +{ + uint16_t numArgs = 1; + + nsCSSValue fillRuleValue; + if (ParseEnum(fillRuleValue, nsCSSProps::kFillRuleKTable)) { + numArgs++; + + // The fill-rule must be comma separated from the polygon points. + if (!ExpectSymbol(',', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedComma); + SkipUntil(')'); + return false; + } + } + + nsCSSValue coordinates; + nsCSSValuePairList* item = coordinates.SetPairListValue(); + for (;;) { + nsCSSValue xValue, yValue; + if (ParseVariant(xValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok || + ParseVariant(yValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok) { + REPORT_UNEXPECTED_TOKEN(PECoordinatePair); + SkipUntil(')'); + return false; + } + item->mXValue = xValue; + item->mYValue = yValue; + + // See whether to continue or whether to look for end of function. + if (!ExpectSymbol(',', true)) { + // We need to read the closing parenthesis. + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); + SkipUntil(')'); + return false; + } + break; + } + item->mNext = new nsCSSValuePairList; + item = item->mNext; + } + + RefPtr functionArray = + aValue.InitFunction(eCSSKeyword_polygon, numArgs); + functionArray->Item(numArgs) = coordinates; + if (numArgs > 1) { + functionArray->Item(1) = fillRuleValue; + } + + return true; +} + +bool +CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword, + nsCSSValue& aValue) +{ + nsCSSValue radiusX, radiusY, position; + bool hasRadius = false, hasPosition = false; + + int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION | + VARIANT_KEYWORD; + CSSParseResult result = + ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + if (aKeyword == eCSSKeyword_ellipse) { + if (ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable) != + CSSParseResult::Ok) { + REPORT_UNEXPECTED_TOKEN(PEExpectedRadius); + SkipUntil(')'); + return false; + } + } + hasRadius = true; + } + + if (!ExpectSymbol(')', true)) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEPositionEOF); + return false; + } + + if (mToken.mType != eCSSToken_Ident || + !mToken.mIdent.LowerCaseEqualsLiteral("at") || + !ParsePositionValueForBasicShape(position)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedPosition); + SkipUntil(')'); + return false; + } + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); + SkipUntil(')'); + return false; + } + hasPosition = true; + } + + size_t count = aKeyword == eCSSKeyword_circle ? 2 : 3; + RefPtr functionArray = + aValue.InitFunction(aKeyword, count); + if (hasRadius) { + functionArray->Item(1) = radiusX; + if (aKeyword == eCSSKeyword_ellipse) { + functionArray->Item(2) = radiusY; + } + } + if (hasPosition) { + functionArray->Item(count) = position; + } + + return true; +} + +bool +CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue) +{ + RefPtr functionArray = + aValue.InitFunction(eCSSKeyword_inset, 5); + + int count = 0; + while (count < 4) { + CSSParseResult result = + ParseVariant(functionArray->Item(count + 1), VARIANT_LPCALC, nullptr); + if (result == CSSParseResult::Error) { + count = 0; + break; + } else if (result == CSSParseResult::NotFound) { + break; + } + ++count; + } + + if (count == 0) { + REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg); + SkipUntil(')'); + return false; + } + + if (!ExpectSymbol(')', true)) { + if (!GetToken(true)) { + NS_NOTREACHED("ExpectSymbol should have returned true"); + return false; + } + + RefPtr radiusArray = nsCSSValue::Array::Create(4); + functionArray->Item(5).SetArrayValue(radiusArray, eCSSUnit_Array); + if (mToken.mType != eCSSToken_Ident || + !mToken.mIdent.LowerCaseEqualsLiteral("round") || + !ParseBoxCornerRadiiInternals(radiusArray->ItemStorage())) { + REPORT_UNEXPECTED_TOKEN(PEExpectedRadius); + SkipUntil(')'); + return false; + } + + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedCloseParen); + SkipUntil(')'); + return false; + } + } + + return true; +} + +bool +CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens) +{ + if (!GetToken(true)) { + return false; + } + + if (mToken.mType != eCSSToken_Function) { + UngetToken(); + return false; + } + + // Specific shape function parsing always consumes tokens. + *aConsumedTokens = true; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + switch (keyword) { + case eCSSKeyword_polygon: + return ParsePolygonFunction(aValue); + case eCSSKeyword_circle: + case eCSSKeyword_ellipse: + return ParseCircleOrEllipseFunction(keyword, aValue); + case eCSSKeyword_inset: + return ParseInsetFunction(aValue); + default: + return false; + } +} + +bool +CSSParserImpl::ParseReferenceBoxAndBasicShape( + nsCSSValue& aValue, + const KTableEntry aBoxKeywordTable[]) +{ + nsCSSValue referenceBox; + bool hasBox = ParseEnum(referenceBox, aBoxKeywordTable); + + const bool boxCameFirst = hasBox; + + nsCSSValue basicShape; + bool basicShapeConsumedTokens = false; + bool hasShape = ParseBasicShape(basicShape, &basicShapeConsumedTokens); + + // Parsing wasn't successful if ParseBasicShape consumed tokens but failed + // or if the token was neither a reference box nor a basic shape. + if ((!hasShape && basicShapeConsumedTokens) || (!hasBox && !hasShape)) { + return false; + } + + // Check if the second argument is a reference box if the first wasn't. + if (!hasBox) { + hasBox = ParseEnum(referenceBox, aBoxKeywordTable); + } + + RefPtr fullValue = + nsCSSValue::Array::Create((hasBox && hasShape) ? 2 : 1); + + if (hasBox && hasShape) { + fullValue->Item(boxCameFirst ? 0 : 1) = referenceBox; + fullValue->Item(boxCameFirst ? 1 : 0) = basicShape; + } else if (hasBox) { + fullValue->Item(0) = referenceBox; + } else { + MOZ_ASSERT(hasShape, "should've bailed if we got neither box nor shape"); + fullValue->Item(0) = basicShape; + } + + aValue.SetArrayValue(fullValue, eCSSUnit_Array); + return true; +} + +// Parse a clip-path url to a element or a basic shape. +bool +CSSParserImpl::ParseClipPath(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) { + return true; + } + + if (!nsLayoutUtils::CSSClipPathShapesEnabled()) { + // With CSS Clip Path Shapes disabled, we should only accept + // SVG clipPath reference and none. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL); + return false; + } + + return ParseReferenceBoxAndBasicShape( + aValue, nsCSSProps::kClipPathGeometryBoxKTable); +} + +// none | [ || ] | +bool +CSSParserImpl::ParseShapeOutside(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_HUO, nullptr)) { + // 'inherit', 'initial', 'unset', 'none', and url must be alone. + return true; + } + + return ParseReferenceBoxAndBasicShape( + aValue, nsCSSProps::kShapeOutsideShapeBoxKTable); +} + +bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) +{ + nsCSSValuePair position; + if (!ParseBoxPositionValues(position, true)) + return false; + + nsCSSPropertyID prop = eCSSProperty_transform_origin; + if (aPerspective) { + prop = eCSSProperty_perspective_origin; + } + + // Unlike many other uses of pairs, this position should always be stored + // as a pair, even if the values are the same, so it always serializes as + // a pair, and to keep the computation code simple. + if (position.mXValue.GetUnit() == eCSSUnit_Inherit || + position.mXValue.GetUnit() == eCSSUnit_Initial || + position.mXValue.GetUnit() == eCSSUnit_Unset) { + MOZ_ASSERT(position.mXValue == position.mYValue, + "inherit/initial/unset only half?"); + AppendValue(prop, position.mXValue); + } else { + nsCSSValue value; + if (aPerspective) { + value.SetPairValue(position.mXValue, position.mYValue); + } else { + nsCSSValue depth; + CSSParseResult result = + ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + depth.SetFloatValue(0.0f, eCSSUnit_Pixel); + } + value.SetTripletValue(position.mXValue, position.mYValue, depth); + } + + AppendValue(prop, value); + } + return true; +} + +/** + * Reads a drop-shadow value. At the moment the Filter Effects specification + * just expects one shadow item. Should this ever change to a list of shadow + * items, use ParseShadowList instead. + */ +bool +CSSParserImpl::ParseDropShadow(nsCSSValue* aValue) +{ + // Use nsCSSValueList to reuse the shadow resolving code in + // nsRuleNode and nsComputedDOMStyle. + nsCSSValue shadow; + nsCSSValueList* cur = shadow.SetListValue(); + if (!ParseShadowItem(cur->mValue, false)) + return false; + + if (!ExpectSymbol(')', true)) + return false; + + nsCSSValue::Array* dropShadow = aValue->InitFunction(eCSSKeyword_drop_shadow, 1); + + // Copy things over. + dropShadow->Item(1) = shadow; + + return true; +} + +/** + * Reads a single url or filter function from the tokenizer stream, reporting an + * error if something goes wrong. + */ +bool +CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) +{ + if (ParseSingleTokenVariant(*aValue, VARIANT_URL, nullptr)) { + return true; + } + + if (!nsLayoutUtils::CSSFiltersEnabled()) { + // With CSS Filters disabled, we should only accept an SVG reference filter. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL); + return false; + } + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFilterEOF); + return false; + } + + if (mToken.mType != eCSSToken_Function) { + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + UngetToken(); + return false; + } + + nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent); + // Parse drop-shadow independently of the other filter functions + // because of its more complex characteristics. + if (functionName == eCSSKeyword_drop_shadow) { + if (ParseDropShadow(aValue)) { + return true; + } else { + // Unrecognized filter function. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + SkipUntil(')'); + return false; + } + } + + // Set up the parsing rules based on the filter function. + uint32_t variantMask = VARIANT_PN; + bool rejectNegativeArgument = true; + bool clampArgumentToOne = false; + switch (functionName) { + case eCSSKeyword_blur: + variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION; + // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths. + rejectNegativeArgument = false; + break; + case eCSSKeyword_brightness: + case eCSSKeyword_contrast: + case eCSSKeyword_saturate: + break; + case eCSSKeyword_grayscale: + case eCSSKeyword_invert: + case eCSSKeyword_sepia: + case eCSSKeyword_opacity: + clampArgumentToOne = true; + break; + case eCSSKeyword_hue_rotate: + variantMask = VARIANT_ANGLE; + rejectNegativeArgument = false; + break; + default: + // Unrecognized filter function. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + SkipUntil(')'); + return false; + } + + // Parse the function. + uint16_t minElems = 1U; + uint16_t maxElems = 1U; + uint32_t allVariants = 0; + if (!ParseFunction(functionName, &variantMask, allVariants, + minElems, maxElems, *aValue)) { + REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError); + return false; + } + + // Get the first and only argument to the filter function. + MOZ_ASSERT(aValue->GetUnit() == eCSSUnit_Function, + "expected a filter function"); + MOZ_ASSERT(aValue->UnitHasArrayValue(), + "filter function should be an array"); + MOZ_ASSERT(aValue->GetArrayValue()->Count() == 2, + "filter function should have exactly one argument"); + nsCSSValue& arg = aValue->GetArrayValue()->Item(1); + + if (rejectNegativeArgument && + ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) || + (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) { + REPORT_UNEXPECTED(PEExpectedNonnegativeNP); + return false; + } + + if (clampArgumentToOne) { + if (arg.GetUnit() == eCSSUnit_Number && + arg.GetFloatValue() > 1.0f) { + arg.SetFloatValue(1.0f, arg.GetUnit()); + } else if (arg.GetUnit() == eCSSUnit_Percent && + arg.GetPercentValue() > 1.0f) { + arg.SetPercentValue(1.0f); + } + } + + return true; +} + +/** + * Parses a filter property value by continuously reading in urls and/or filter + * functions and constructing a list. + * + * When CSS Filters are enabled, the filter property accepts one or more SVG + * reference filters and/or CSS filter functions. + * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%); + * + * When CSS Filters are disabled, the filter property only accepts one SVG + * reference filter. + * e.g. filter: url(#my-filter); + */ +bool +CSSParserImpl::ParseFilter() +{ + nsCSSValue value; + // 'inherit', 'initial', 'unset' and 'none' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + nsCSSValueList* cur = value.SetListValue(); + while (cur) { + if (!ParseSingleFilter(&cur->mValue)) { + return false; + } + if (CheckEndProperty()) { + break; + } + if (!nsLayoutUtils::CSSFiltersEnabled()) { + // With CSS Filters disabled, we should only accept one SVG reference + // filter. + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + return false; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_filter, value); + return true; +} + +bool +CSSParserImpl::ParseTransitionProperty() +{ + nsCSSValue value; + // 'inherit', 'initial', 'unset' and 'none' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + // Accept a list of arbitrary identifiers. They should be + // CSS properties, but we want to accept any so that we + // accept properties that we don't know about yet, e.g. + // transition-property: invalid-property, left, opacity; + nsCSSValueList* cur = value.SetListValue(); + for (;;) { + if (!ParseSingleTokenVariant(cur->mValue, + VARIANT_IDENTIFIER | VARIANT_ALL, + nullptr)) { + return false; + } + if (cur->mValue.GetUnit() == eCSSUnit_Ident) { + nsDependentString str(cur->mValue.GetStringBufferValue()); + // Exclude 'none', 'inherit', 'initial' and 'unset' according to the + // same rules as for 'counter-reset' in CSS 2.1. + if (str.LowerCaseEqualsLiteral("none") || + str.LowerCaseEqualsLiteral("inherit") || + str.LowerCaseEqualsLiteral("initial") || + (str.LowerCaseEqualsLiteral("unset") && + nsLayoutUtils::UnsetValueEnabled())) { + return false; + } + } + if (!ExpectSymbol(',', true)) { + break; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_transition_property, value); + return true; +} + +bool +CSSParserImpl::ParseTransitionTimingFunctionValues(nsCSSValue& aValue) +{ + NS_ASSERTION(!mHavePushBack && + mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("cubic-bezier"), + "unexpected initial state"); + + RefPtr val = nsCSSValue::Array::Create(4); + + float x1, x2, y1, y2; + if (!ParseTransitionTimingFunctionValueComponent(x1, ',', true) || + !ParseTransitionTimingFunctionValueComponent(y1, ',', false) || + !ParseTransitionTimingFunctionValueComponent(x2, ',', true) || + !ParseTransitionTimingFunctionValueComponent(y2, ')', false)) { + return false; + } + + val->Item(0).SetFloatValue(x1, eCSSUnit_Number); + val->Item(1).SetFloatValue(y1, eCSSUnit_Number); + val->Item(2).SetFloatValue(x2, eCSSUnit_Number); + val->Item(3).SetFloatValue(y2, eCSSUnit_Number); + + aValue.SetArrayValue(val, eCSSUnit_Cubic_Bezier); + + return true; +} + +bool +CSSParserImpl::ParseTransitionTimingFunctionValueComponent(float& aComponent, + char aStop, + bool aIsXPoint) +{ + if (!GetToken(true)) { + return false; + } + nsCSSToken* tk = &mToken; + if (tk->mType == eCSSToken_Number) { + float num = tk->mNumber; + + // Clamp infinity or -infinity values to max float or -max float to avoid + // calculations with infinity. + num = mozilla::clamped(num, -std::numeric_limits::max(), + std::numeric_limits::max()); + + // X control point should be inside [0, 1] range. + if (aIsXPoint && (num < 0.0 || num > 1.0)) { + return false; + } + aComponent = num; + if (ExpectSymbol(aStop, true)) { + return true; + } + } + return false; +} + +bool +CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue) +{ + NS_ASSERTION(!mHavePushBack && + mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("steps"), + "unexpected initial state"); + + RefPtr val = nsCSSValue::Array::Create(2); + + if (!ParseSingleTokenOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, + nullptr)) { + return false; + } + + int32_t type = -1; // indicates an implicit end value + if (ExpectSymbol(',', true)) { + if (!GetToken(true)) { + return false; + } + if (mToken.mType == eCSSToken_Ident) { + if (mToken.mIdent.LowerCaseEqualsLiteral("start")) { + type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("end")) { + type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END; + } + } + if (type == -1) { + UngetToken(); + return false; + } + } + val->Item(1).SetIntValue(type, eCSSUnit_Enumerated); + + if (!ExpectSymbol(')', true)) { + return false; + } + + aValue.SetArrayValue(val, eCSSUnit_Steps); + return true; +} + +static nsCSSValueList* +AppendValueToList(nsCSSValue& aContainer, + nsCSSValueList* aTail, + const nsCSSValue& aValue) +{ + nsCSSValueList* entry; + if (aContainer.GetUnit() == eCSSUnit_Null) { + MOZ_ASSERT(!aTail, "should not have an entry"); + entry = aContainer.SetListValue(); + } else { + MOZ_ASSERT(!aTail->mNext, "should not have a next entry"); + MOZ_ASSERT(aContainer.GetUnit() == eCSSUnit_List, "not a list"); + entry = new nsCSSValueList; + aTail->mNext = entry; + } + entry->mValue = aValue; + return entry; +} + +CSSParserImpl::ParseAnimationOrTransitionShorthandResult +CSSParserImpl::ParseAnimationOrTransitionShorthand( + const nsCSSPropertyID* aProperties, + const nsCSSValue* aInitialValues, + nsCSSValue* aValues, + size_t aNumProperties) +{ + nsCSSValue tempValue; + // first see if 'inherit', 'initial' or 'unset' is specified. If one is, + // it can be the only thing specified, so don't attempt to parse any + // additional properties + if (ParseSingleTokenVariant(tempValue, VARIANT_INHERIT, nullptr)) { + for (uint32_t i = 0; i < aNumProperties; ++i) { + AppendValue(aProperties[i], tempValue); + } + return eParseAnimationOrTransitionShorthand_Inherit; + } + + static const size_t maxNumProperties = 8; + MOZ_ASSERT(aNumProperties <= maxNumProperties, + "can't handle this many properties"); + nsCSSValueList *cur[maxNumProperties]; + bool parsedProperty[maxNumProperties]; + + for (size_t i = 0; i < aNumProperties; ++i) { + cur[i] = nullptr; + } + bool atEOP = false; // at end of property? + for (;;) { // loop over comma-separated transitions or animations + // whether a particular subproperty was specified for this + // transition or animation + bool haveAnyProperty = false; + for (size_t i = 0; i < aNumProperties; ++i) { + parsedProperty[i] = false; + } + for (;;) { // loop over values within a transition or animation + bool foundProperty = false; + // check to see if we're at the end of one full transition or + // animation definition (either because we hit a comma or because + // we hit the end of the property definition) + if (ExpectSymbol(',', true)) + break; + if (CheckEndProperty()) { + atEOP = true; + break; + } + + // else, try to parse the next transition or animation sub-property + for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) { + if (!parsedProperty[i]) { + // if we haven't found this property yet, try to parse it + CSSParseResult result = + ParseSingleValueProperty(tempValue, aProperties[i]); + if (result == CSSParseResult::Error) { + return eParseAnimationOrTransitionShorthand_Error; + } + if (result == CSSParseResult::Ok) { + parsedProperty[i] = true; + cur[i] = AppendValueToList(aValues[i], cur[i], tempValue); + foundProperty = true; + haveAnyProperty = true; + break; // out of inner loop; continue looking for next sub-property + } + } + } + if (!foundProperty) { + // We're not at a ',' or at the end of the property, but we couldn't + // parse any of the sub-properties, so the declaration is invalid. + return eParseAnimationOrTransitionShorthand_Error; + } + } + + if (!haveAnyProperty) { + // Got an empty item. + return eParseAnimationOrTransitionShorthand_Error; + } + + // We hit the end of the property or the end of one transition + // or animation definition, add its components to the list. + for (uint32_t i = 0; i < aNumProperties; ++i) { + // If all of the subproperties were not explicitly specified, fill + // in the missing ones with initial values. + if (!parsedProperty[i]) { + cur[i] = AppendValueToList(aValues[i], cur[i], aInitialValues[i]); + } + } + + if (atEOP) + break; + // else we just hit a ',' so continue parsing the next compound transition + } + + return eParseAnimationOrTransitionShorthand_Values; +} + +bool +CSSParserImpl::ParseTransition() +{ + static const nsCSSPropertyID kTransitionProperties[] = { + eCSSProperty_transition_duration, + eCSSProperty_transition_timing_function, + // Must check 'transition-delay' after 'transition-duration', since + // that's our assumption about what the spec means for the shorthand + // syntax (the first time given is the duration, and the second + // given is the delay). + eCSSProperty_transition_delay, + // Must check 'transition-property' after + // 'transition-timing-function' since 'transition-property' accepts + // any keyword. + eCSSProperty_transition_property + }; + static const uint32_t numProps = MOZ_ARRAY_LENGTH(kTransitionProperties); + // this is a shorthand property that accepts -property, -delay, + // -duration, and -timing-function with some components missing. + // there can be multiple transitions, separated with commas + + nsCSSValue initialValues[numProps]; + initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds); + initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE, + eCSSUnit_Enumerated); + initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds); + initialValues[3].SetAllValue(); + + nsCSSValue values[numProps]; + + ParseAnimationOrTransitionShorthandResult spres = + ParseAnimationOrTransitionShorthand(kTransitionProperties, + initialValues, values, numProps); + if (spres != eParseAnimationOrTransitionShorthand_Values) { + return spres != eParseAnimationOrTransitionShorthand_Error; + } + + // Make two checks on the list for 'transition-property': + // + If there is more than one item, then none of the items can be + // 'none'. + // + None of the items can be 'inherit', 'initial' or 'unset'. + { + MOZ_ASSERT(kTransitionProperties[3] == eCSSProperty_transition_property, + "array index mismatch"); + nsCSSValueList *l = values[3].GetListValue(); + bool multipleItems = !!l->mNext; + do { + const nsCSSValue& val = l->mValue; + if (val.GetUnit() == eCSSUnit_None) { + if (multipleItems) { + // This is a syntax error. + return false; + } + + // Unbox a solitary 'none'. + values[3].SetNoneValue(); + break; + } + if (val.GetUnit() == eCSSUnit_Ident) { + nsDependentString str(val.GetStringBufferValue()); + if (str.EqualsLiteral("inherit") || + str.EqualsLiteral("initial") || + (str.EqualsLiteral("unset") && + nsLayoutUtils::UnsetValueEnabled())) { + return false; + } + } + } while ((l = l->mNext)); + } + + // Save all parsed transition sub-properties in mTempData + for (uint32_t i = 0; i < numProps; ++i) { + AppendValue(kTransitionProperties[i], values[i]); + } + return true; +} + +bool +CSSParserImpl::ParseAnimation() +{ + static const nsCSSPropertyID kAnimationProperties[] = { + eCSSProperty_animation_duration, + eCSSProperty_animation_timing_function, + // Must check 'animation-delay' after 'animation-duration', since + // that's our assumption about what the spec means for the shorthand + // syntax (the first time given is the duration, and the second + // given is the delay). + eCSSProperty_animation_delay, + eCSSProperty_animation_direction, + eCSSProperty_animation_fill_mode, + eCSSProperty_animation_iteration_count, + eCSSProperty_animation_play_state, + // Must check 'animation-name' after 'animation-timing-function', + // 'animation-direction', 'animation-fill-mode', + // 'animation-iteration-count', and 'animation-play-state' since + // 'animation-name' accepts any keyword. + eCSSProperty_animation_name + }; + static const uint32_t numProps = MOZ_ARRAY_LENGTH(kAnimationProperties); + // this is a shorthand property that accepts -property, -delay, + // -duration, and -timing-function with some components missing. + // there can be multiple animations, separated with commas + + nsCSSValue initialValues[numProps]; + initialValues[0].SetFloatValue(0.0, eCSSUnit_Seconds); + initialValues[1].SetIntValue(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE, + eCSSUnit_Enumerated); + initialValues[2].SetFloatValue(0.0, eCSSUnit_Seconds); + initialValues[3].SetIntValue(static_cast(mozilla::dom::PlaybackDirection::Normal), + eCSSUnit_Enumerated); + initialValues[4].SetIntValue(static_cast(mozilla::dom::FillMode::None), + eCSSUnit_Enumerated); + initialValues[5].SetFloatValue(1.0f, eCSSUnit_Number); + initialValues[6].SetIntValue(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING, eCSSUnit_Enumerated); + initialValues[7].SetNoneValue(); + + nsCSSValue values[numProps]; + + ParseAnimationOrTransitionShorthandResult spres = + ParseAnimationOrTransitionShorthand(kAnimationProperties, + initialValues, values, numProps); + if (spres != eParseAnimationOrTransitionShorthand_Values) { + return spres != eParseAnimationOrTransitionShorthand_Error; + } + + // Save all parsed animation sub-properties in mTempData + for (uint32_t i = 0; i < numProps; ++i) { + AppendValue(kAnimationProperties[i], values[i]); + } + return true; +} + +bool +CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow) +{ + // A shadow list item is an array, with entries in this sequence: + enum { + IndexX, + IndexY, + IndexRadius, + IndexSpread, // only for box-shadow + IndexColor, + IndexInset // only for box-shadow + }; + + RefPtr val = nsCSSValue::Array::Create(6); + + if (aIsBoxShadow) { + // Optional inset keyword (ignore errors) + ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD, + nsCSSProps::kBoxShadowTypeKTable); + } + + nsCSSValue xOrColor; + bool haveColor = false; + if (ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { + return false; + } + if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) { + val->Item(IndexX) = xOrColor; + } else { + // Must be a color (as string or color value) + NS_ASSERTION(xOrColor.GetUnit() == eCSSUnit_Ident || + xOrColor.GetUnit() == eCSSUnit_EnumColor || + xOrColor.IsNumericColorUnit(), + "Must be a color value"); + val->Item(IndexColor) = xOrColor; + haveColor = true; + + // X coordinate mandatory after color + if (ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { + return false; + } + } + + // Y coordinate; mandatory + if (ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { + return false; + } + + // Optional radius. Ignore errors except if they pass a negative + // value which we must reject. If we use ParseNonNegativeVariant + // we can't tell the difference between an unspecified radius + // and a negative radius. + CSSParseResult result = + ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC, + nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + if (val->Item(IndexRadius).IsLengthUnit() && + val->Item(IndexRadius).GetFloatValue() < 0) { + return false; + } + } + + if (aIsBoxShadow) { + // Optional spread + if (ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, + nullptr) == CSSParseResult::Error) { + return false; + } + } + + if (!haveColor) { + // Optional color + if (ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr) == + CSSParseResult::Error) { + return false; + } + } + + if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) { + // Optional inset keyword + ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD, + nsCSSProps::kBoxShadowTypeKTable); + } + + aValue.SetArrayValue(val, eCSSUnit_Array); + return true; +} + +bool +CSSParserImpl::ParseShadowList(nsCSSPropertyID aProperty) +{ + nsAutoParseCompoundProperty compound(this); + bool isBoxShadow = aProperty == eCSSProperty_box_shadow; + + nsCSSValue value; + // 'inherit', 'initial', 'unset' and 'none' must be alone + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + nsCSSValueList* cur = value.SetListValue(); + for (;;) { + if (!ParseShadowItem(cur->mValue, isBoxShadow)) { + return false; + } + if (!ExpectSymbol(',', true)) { + break; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(aProperty, value); + return true; +} + +int32_t +CSSParserImpl::GetNamespaceIdForPrefix(const nsString& aPrefix) +{ + NS_PRECONDITION(!aPrefix.IsEmpty(), "Must have a prefix here"); + + int32_t nameSpaceID = kNameSpaceID_Unknown; + if (mNameSpaceMap) { + // user-specified identifiers are case-sensitive (bug 416106) + nsCOMPtr prefix = NS_Atomize(aPrefix); + nameSpaceID = mNameSpaceMap->FindNameSpaceID(prefix); + } + // else no declared namespaces + + if (nameSpaceID == kNameSpaceID_Unknown) { // unknown prefix, dump it + REPORT_UNEXPECTED_P(PEUnknownNamespacePrefix, aPrefix); + } + + return nameSpaceID; +} + +void +CSSParserImpl::SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector) +{ + if (mNameSpaceMap) { + aSelector.SetNameSpace(mNameSpaceMap->FindNameSpaceID(nullptr)); + } else { + aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard + } +} + +bool +CSSParserImpl::ParsePaint(nsCSSPropertyID aPropID) +{ + nsCSSValue x, y; + + if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | + VARIANT_OPENTYPE_SVG_KEYWORD, + nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) { + return false; + } + + bool canHaveFallback = x.GetUnit() == eCSSUnit_URL || + x.GetUnit() == eCSSUnit_Enumerated; + if (canHaveFallback) { + CSSParseResult result = + ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { + y.SetNoneValue(); + } + } + + if (!canHaveFallback) { + AppendValue(aPropID, x); + } else { + nsCSSValue val; + val.SetPairValue(x, y); + AppendValue(aPropID, val); + } + return true; +} + +bool +CSSParserImpl::ParseDasharray() +{ + nsCSSValue value; + + // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE | + VARIANT_OPENTYPE_SVG_KEYWORD, + nsCSSProps::kStrokeContextValueKTable)) { + nsCSSValueList *cur = value.SetListValue(); + for (;;) { + if (!ParseSingleTokenNonNegativeVariant(cur->mValue, VARIANT_LPN, + nullptr)) { + return false; + } + if (CheckEndProperty()) { + break; + } + // skip optional commas between elements + (void)ExpectSymbol(',', true); + + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_stroke_dasharray, value); + return true; +} + +bool +CSSParserImpl::ParseMarker() +{ + nsCSSValue marker; + if (ParseSingleValueProperty(marker, eCSSProperty_marker_end) == + CSSParseResult::Ok) { + AppendValue(eCSSProperty_marker_end, marker); + AppendValue(eCSSProperty_marker_mid, marker); + AppendValue(eCSSProperty_marker_start, marker); + return true; + } + return false; +} + +bool +CSSParserImpl::ParsePaintOrder() +{ + static_assert + ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) > NS_STYLE_PAINT_ORDER_LAST_VALUE, + "bitfield width insufficient for paint-order constants"); + + static const KTableEntry kPaintOrderKTable[] = { + { eCSSKeyword_normal, NS_STYLE_PAINT_ORDER_NORMAL }, + { eCSSKeyword_fill, NS_STYLE_PAINT_ORDER_FILL }, + { eCSSKeyword_stroke, NS_STYLE_PAINT_ORDER_STROKE }, + { eCSSKeyword_markers, NS_STYLE_PAINT_ORDER_MARKERS }, + { eCSSKeyword_UNKNOWN, -1 } + }; + + static_assert(MOZ_ARRAY_LENGTH(kPaintOrderKTable) == + NS_STYLE_PAINT_ORDER_LAST_VALUE + 2, + "missing paint-order values in kPaintOrderKTable"); + + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_HK, kPaintOrderKTable)) { + return false; + } + + uint32_t seen = 0; + uint32_t order = 0; + uint32_t position = 0; + + // Ensure that even cast to a signed int32_t when stored in CSSValue, + // we have enough space for the entire paint-order value. + static_assert + (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE < 32, + "seen and order not big enough"); + + if (value.GetUnit() == eCSSUnit_Enumerated) { + uint32_t component = static_cast(value.GetIntValue()); + if (component != NS_STYLE_PAINT_ORDER_NORMAL) { + bool parsedOK = true; + for (;;) { + if (seen & (1 << component)) { + // Already seen this component. + UngetToken(); + parsedOK = false; + break; + } + seen |= (1 << component); + order |= (component << position); + position += NS_STYLE_PAINT_ORDER_BITWIDTH; + if (!ParseEnum(value, kPaintOrderKTable)) { + break; + } + component = value.GetIntValue(); + if (component == NS_STYLE_PAINT_ORDER_NORMAL) { + // Can't have "normal" in the middle of the list of paint components. + UngetToken(); + parsedOK = false; + break; + } + } + + // Fill in the remaining paint-order components in the order of their + // constant values. + if (parsedOK) { + for (component = 1; + component <= NS_STYLE_PAINT_ORDER_LAST_VALUE; + component++) { + if (!(seen & (1 << component))) { + order |= (component << position); + position += NS_STYLE_PAINT_ORDER_BITWIDTH; + } + } + } + } + + static_assert(NS_STYLE_PAINT_ORDER_NORMAL == 0, + "unexpected value for NS_STYLE_PAINT_ORDER_NORMAL"); + value.SetIntValue(static_cast(order), eCSSUnit_Enumerated); + } + + AppendValue(eCSSProperty_paint_order, value); + return true; +} + +bool +CSSParserImpl::BackslashDropped() +{ + return mScanner->GetEOFCharacters() & + nsCSSScanner::eEOFCharacters_DropBackslash; +} + +void +CSSParserImpl::AppendImpliedEOFCharacters(nsAString& aResult) +{ + nsCSSScanner::AppendImpliedEOFCharacters(mScanner->GetEOFCharacters(), + aResult); +} + +bool +CSSParserImpl::ParseAll() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { + return false; + } + + // It's unlikely we'll want to use 'all' from within a UA style sheet, so + // instead of computing the correct EnabledState value we just expand out + // to all content-visible properties. + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all, + CSSEnabledState::eForAllContent) { + AppendValue(*p, value); + } + return true; +} + +bool +CSSParserImpl::ParseVariableDeclaration(CSSVariableDeclarations::Type* aType, + nsString& aValue) +{ + CSSVariableDeclarations::Type type; + nsString variableValue; + bool dropBackslash; + nsString impliedCharacters; + + // Record the token stream while parsing a variable value. + if (!mInSupportsCondition) { + mScanner->StartRecording(); + } + if (!ParseValueWithVariables(&type, &dropBackslash, impliedCharacters, + nullptr, nullptr)) { + if (!mInSupportsCondition) { + mScanner->StopRecording(); + } + return false; + } + + if (!mInSupportsCondition) { + if (type == CSSVariableDeclarations::eTokenStream) { + // This was indeed a token stream value, so store it in variableValue. + mScanner->StopRecording(variableValue); + if (dropBackslash) { + MOZ_ASSERT(!variableValue.IsEmpty() && + variableValue[variableValue.Length() - 1] == '\\'); + variableValue.Truncate(variableValue.Length() - 1); + } + variableValue.Append(impliedCharacters); + } else { + // This was either 'inherit' or 'initial'; we don't need the recorded + // input. + mScanner->StopRecording(); + } + } + + if (mHavePushBack && type == CSSVariableDeclarations::eTokenStream) { + // If we came to the end of a valid variable declaration and a token was + // pushed back, then it would have been ended by '!', ')', ';', ']' or '}'. + // We need to remove it from the recorded variable value. + MOZ_ASSERT(mToken.IsSymbol('!') || + mToken.IsSymbol(')') || + mToken.IsSymbol(';') || + mToken.IsSymbol(']') || + mToken.IsSymbol('}')); + if (!mInSupportsCondition) { + MOZ_ASSERT(!variableValue.IsEmpty()); + MOZ_ASSERT(variableValue[variableValue.Length() - 1] == mToken.mSymbol); + variableValue.Truncate(variableValue.Length() - 1); + } + } + + *aType = type; + aValue = variableValue; + return true; +} + +bool +CSSParserImpl::ParseScrollSnapType() +{ + nsCSSValue value; + if (!ParseSingleTokenVariant(value, VARIANT_HK, + nsCSSProps::kScrollSnapTypeKTable)) { + return false; + } + AppendValue(eCSSProperty_scroll_snap_type_x, value); + AppendValue(eCSSProperty_scroll_snap_type_y, value); + return true; +} + +bool +CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSPropertyID aPropID) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + return true; + } + if (!GetToken(true)) { + return false; + } + if (mToken.mType == eCSSToken_Function && + nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) { + nsCSSValue lengthValue; + if (ParseNonNegativeVariant(lengthValue, + VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { + REPORT_UNEXPECTED(PEExpectedNonnegativeNP); + SkipUntil(')'); + return false; + } + if (!ExpectSymbol(')', true)) { + REPORT_UNEXPECTED(PEExpectedCloseParen); + SkipUntil(')'); + return false; + } + RefPtr functionArray = + aValue.InitFunction(eCSSKeyword_repeat, 1); + functionArray->Item(1) = lengthValue; + return true; + } + UngetToken(); + return false; +} + + +bool +CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { + return true; + } + nsCSSValue itemValue; + if (!ParsePositionValue(aValue)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedPosition); + return false; + } + return true; +} + +// This function is very similar to ParseImageLayerPosition, and ParseImageLayerSize. +bool +CSSParserImpl::ParseScrollSnapCoordinate(nsCSSValue& aValue) +{ + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { + return true; + } + nsCSSValue itemValue; + if (!ParsePositionValue(itemValue)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedPosition); + return false; + } + nsCSSValueList* item = aValue.SetListValue(); + for (;;) { + item->mValue = itemValue; + if (!ExpectSymbol(',', true)) { + break; + } + if (!ParsePositionValue(itemValue)) { + REPORT_UNEXPECTED_TOKEN(PEExpectedPosition); + return false; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + } + return true; +} + +bool +CSSParserImpl::ParseValueWithVariables(CSSVariableDeclarations::Type* aType, + bool* aDropBackslash, + nsString& aImpliedCharacters, + void (*aFunc)(const nsAString&, void*), + void* aData) +{ + // A property value is invalid if it contains variable references and also: + // + // * has unbalanced parens, brackets or braces + // * has any BAD_STRING or BAD_URL tokens + // * has any ';' or '!' tokens at the top level of a variable reference's + // fallback + // + // If the property is a custom property (i.e. a variable declaration), then + // it is also invalid if it consists of no tokens, such as: + // + // --invalid:; + // + // Note that is valid for a custom property to have a value that consists + // solely of white space, such as: + // + // --valid: ; + + // Stack of closing characters for currently open constructs. + StopSymbolCharStack stack; + + // Indexes into ')' characters in |stack| that correspond to "var(". This + // is used to stop parsing when we encounter a '!' or ';' at the top level + // of a variable reference's fallback. + AutoTArray references; + + if (!GetToken(false)) { + // Variable value was empty since we reached EOF. + REPORT_UNEXPECTED_EOF(PEVariableEOF); + return false; + } + + if (mToken.mType == eCSSToken_Symbol && + (mToken.mSymbol == '!' || + mToken.mSymbol == ')' || + mToken.mSymbol == ';' || + mToken.mSymbol == ']' || + mToken.mSymbol == '}')) { + // Variable value was empty since we reached the end of the construct. + UngetToken(); + REPORT_UNEXPECTED_TOKEN(PEVariableEmpty); + return false; + } + + if (mToken.mType == eCSSToken_Whitespace) { + if (!GetToken(true)) { + // Variable value was white space only. This is valid. + MOZ_ASSERT(!BackslashDropped()); + *aType = CSSVariableDeclarations::eTokenStream; + *aDropBackslash = false; + AppendImpliedEOFCharacters(aImpliedCharacters); + return true; + } + } + + // Look for 'initial', 'inherit' or 'unset' as the first non-white space + // token. + CSSVariableDeclarations::Type type = CSSVariableDeclarations::eTokenStream; + if (mToken.mType == eCSSToken_Ident) { + if (mToken.mIdent.LowerCaseEqualsLiteral("initial")) { + type = CSSVariableDeclarations::eInitial; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("inherit")) { + type = CSSVariableDeclarations::eInherit; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("unset")) { + type = CSSVariableDeclarations::eUnset; + } + } + + if (type != CSSVariableDeclarations::eTokenStream) { + if (!GetToken(true)) { + // Variable value was 'initial' or 'inherit' followed by EOF. + MOZ_ASSERT(!BackslashDropped()); + *aType = type; + *aDropBackslash = false; + AppendImpliedEOFCharacters(aImpliedCharacters); + return true; + } + UngetToken(); + if (mToken.mType == eCSSToken_Symbol && + (mToken.mSymbol == '!' || + mToken.mSymbol == ')' || + mToken.mSymbol == ';' || + mToken.mSymbol == ']' || + mToken.mSymbol == '}')) { + // Variable value was 'initial' or 'inherit' followed by the end + // of the declaration. + MOZ_ASSERT(!BackslashDropped()); + *aType = type; + *aDropBackslash = false; + return true; + } + } + + do { + switch (mToken.mType) { + case eCSSToken_Symbol: + if (mToken.mSymbol == '(') { + stack.AppendElement(')'); + } else if (mToken.mSymbol == '[') { + stack.AppendElement(']'); + } else if (mToken.mSymbol == '{') { + stack.AppendElement('}'); + } else if (mToken.mSymbol == ';' || + mToken.mSymbol == '!') { + if (stack.IsEmpty()) { + UngetToken(); + MOZ_ASSERT(!BackslashDropped()); + *aType = CSSVariableDeclarations::eTokenStream; + *aDropBackslash = false; + return true; + } else if (!references.IsEmpty() && + references.LastElement() == stack.Length() - 1) { + REPORT_UNEXPECTED_TOKEN(PEInvalidVariableTokenFallback); + SkipUntilAllOf(stack); + return false; + } + } else if (mToken.mSymbol == ')' || + mToken.mSymbol == ']' || + mToken.mSymbol == '}') { + for (;;) { + if (stack.IsEmpty()) { + UngetToken(); + MOZ_ASSERT(!BackslashDropped()); + *aType = CSSVariableDeclarations::eTokenStream; + *aDropBackslash = false; + return true; + } + char16_t c = stack.LastElement(); + stack.TruncateLength(stack.Length() - 1); + if (!references.IsEmpty() && + references.LastElement() == stack.Length()) { + references.TruncateLength(references.Length() - 1); + } + if (mToken.mSymbol == c) { + break; + } + } + } + break; + + case eCSSToken_Function: + if (mToken.mIdent.LowerCaseEqualsLiteral("var")) { + if (!GetToken(true)) { + // EOF directly after "var(". + REPORT_UNEXPECTED_EOF(PEExpectedVariableNameEOF); + return false; + } + if (mToken.mType != eCSSToken_Ident || + !nsCSSProps::IsCustomPropertyName(mToken.mIdent)) { + // There must be an identifier directly after the "var(" and + // it must be a custom property name. + UngetToken(); + REPORT_UNEXPECTED_TOKEN(PEExpectedVariableName); + SkipUntil(')'); + SkipUntilAllOf(stack); + return false; + } + if (aFunc) { + MOZ_ASSERT(Substring(mToken.mIdent, 0, + CSS_CUSTOM_NAME_PREFIX_LENGTH). + EqualsLiteral("--")); + // remove '--' + const nsDependentSubstring varName = + Substring(mToken.mIdent, CSS_CUSTOM_NAME_PREFIX_LENGTH); + aFunc(varName, aData); + } + if (!GetToken(true)) { + // EOF right after "var(". + stack.AppendElement(')'); + } else if (mToken.IsSymbol(',')) { + // Variable reference with fallback. + if (!GetToken(false) || mToken.IsSymbol(')')) { + // Comma must be followed by at least one fallback token. + REPORT_UNEXPECTED(PEExpectedVariableFallback); + SkipUntilAllOf(stack); + return false; + } + UngetToken(); + references.AppendElement(stack.Length()); + stack.AppendElement(')'); + } else if (mToken.IsSymbol(')')) { + // Correctly closed variable reference. + } else { + // Malformed variable reference. + REPORT_UNEXPECTED_TOKEN(PEExpectedVariableCommaOrCloseParen); + SkipUntil(')'); + SkipUntilAllOf(stack); + return false; + } + } else { + stack.AppendElement(')'); + } + break; + + case eCSSToken_Bad_String: + SkipUntilAllOf(stack); + return false; + + case eCSSToken_Bad_URL: + SkipUntil(')'); + SkipUntilAllOf(stack); + return false; + + default: + break; + } + } while (GetToken(true)); + + // Append any implied closing characters. + *aDropBackslash = BackslashDropped(); + AppendImpliedEOFCharacters(aImpliedCharacters); + uint32_t i = stack.Length(); + while (i--) { + aImpliedCharacters.Append(stack[i]); + } + + *aType = type; + return true; +} + +bool +CSSParserImpl::IsValueValidForProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue) +{ + mData.AssertInitialState(); + mTempData.AssertInitialState(); + + nsCSSScanner scanner(aPropValue, 0); + css::ErrorReporter reporter(scanner, mSheet, mChildLoader, nullptr); + InitScanner(scanner, reporter, nullptr, nullptr, nullptr); + + // We normally would need to pass in a sheet principal to InitScanner, + // because we might parse a URL value. However, we will never use the + // parsed nsCSSValue (and so whether we have a sheet principal or not + // doesn't really matter), so to avoid failing the assertion in + // SetValueToURL, we set mSheetPrincipalRequired to false to declare + // that it's safe to skip the assertion. + AutoRestore autoRestore(mSheetPrincipalRequired); + mSheetPrincipalRequired = false; + + nsAutoSuppressErrors suppressErrors(this); + + mSection = eCSSSection_General; + scanner.SetSVGMode(false); + + // Check for unknown properties + if (eCSSProperty_UNKNOWN == aPropID) { + ReleaseScanner(); + return false; + } + + // Check that the property and value parse successfully + bool parsedOK = ParseProperty(aPropID); + + // Check for priority + parsedOK = parsedOK && ParsePriority() != ePriority_Error; + + // We should now be at EOF + parsedOK = parsedOK && !GetToken(true); + + mTempData.ClearProperty(aPropID); + mTempData.AssertInitialState(); + mData.AssertInitialState(); + + CLEAR_ERROR(); + ReleaseScanner(); + + return parsedOK; +} + +} // namespace + +// Recycling of parser implementation objects + +static CSSParserImpl* gFreeList = nullptr; + +/* static */ void +nsCSSParser::Startup() +{ + Preferences::AddBoolVarCache(&sOpentypeSVGEnabled, + "gfx.font_rendering.opentype_svg.enabled"); + Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled, + "layout.css.prefixes.webkit"); + Preferences::AddBoolVarCache(&sWebkitDevicePixelRatioEnabled, + "layout.css.prefixes.device-pixel-ratio-webkit"); + Preferences::AddBoolVarCache(&sUnprefixingServiceEnabled, + "layout.css.unprefixing-service.enabled"); +#ifdef NIGHTLY_BUILD + Preferences::AddBoolVarCache(&sUnprefixingServiceGloballyWhitelisted, + "layout.css.unprefixing-service.globally-whitelisted"); +#endif + Preferences::AddBoolVarCache(&sMozGradientsEnabled, + "layout.css.prefixes.gradients"); + Preferences::AddBoolVarCache(&sControlCharVisibility, + "layout.css.control-characters.visible"); +} + +nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader, + CSSStyleSheet* aSheet) +{ + CSSParserImpl *impl = gFreeList; + if (impl) { + gFreeList = impl->mNextFree; + impl->mNextFree = nullptr; + } else { + impl = new CSSParserImpl(); + } + + if (aLoader) { + impl->SetChildLoader(aLoader); + impl->SetQuirkMode(aLoader->GetCompatibilityMode() == + eCompatibility_NavQuirks); + } + if (aSheet) { + impl->SetStyleSheet(aSheet); + } + + mImpl = static_cast(impl); +} + +nsCSSParser::~nsCSSParser() +{ + CSSParserImpl *impl = static_cast(mImpl); + impl->Reset(); + impl->mNextFree = gFreeList; + gFreeList = impl; +} + +/* static */ void +nsCSSParser::Shutdown() +{ + CSSParserImpl *tofree = gFreeList; + CSSParserImpl *next; + while (tofree) + { + next = tofree->mNextFree; + delete tofree; + tofree = next; + } +} + +// Wrapper methods + +nsresult +nsCSSParser::ParseSheet(const nsAString& aInput, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + uint32_t aLineNumber, + css::LoaderReusableStyleSheets* aReusableSheets) +{ + return static_cast(mImpl)-> + ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber, + aReusableSheets); +} + +already_AddRefed +nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue, + nsIURI* aDocURI, + nsIURI* aBaseURI, + nsIPrincipal* aNodePrincipal) +{ + return static_cast(mImpl)-> + ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, aNodePrincipal); +} + +nsresult +nsCSSParser::ParseDeclarations(const nsAString& aBuffer, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged) +{ + return static_cast(mImpl)-> + ParseDeclarations(aBuffer, aSheetURI, aBaseURI, aSheetPrincipal, + aDeclaration, aChanged); +} + +nsresult +nsCSSParser::ParseRule(const nsAString& aRule, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Rule** aResult) +{ + return static_cast(mImpl)-> + ParseRule(aRule, aSheetURI, aBaseURI, aSheetPrincipal, aResult); +} + +void +nsCSSParser::ParseProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant, + bool aIsSVGMode) +{ + static_cast(mImpl)-> + ParseProperty(aPropID, aPropValue, aSheetURI, aBaseURI, + aSheetPrincipal, aDeclaration, aChanged, + aIsImportant, aIsSVGMode); +} + +void +nsCSSParser::ParseLonghandProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aResult) +{ + static_cast(mImpl)-> + ParseLonghandProperty(aPropID, aPropValue, aSheetURI, aBaseURI, + aSheetPrincipal, aResult); +} + +bool +nsCSSParser::ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult) +{ + return static_cast(mImpl)-> + ParseTransformProperty(aPropValue, aDisallowRelativeValues, aResult); +} + +void +nsCSSParser::ParseVariable(const nsAString& aVariableName, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant) +{ + static_cast(mImpl)-> + ParseVariable(aVariableName, aPropValue, aSheetURI, aBaseURI, + aSheetPrincipal, aDeclaration, aChanged, aIsImportant); +} + +void +nsCSSParser::ParseMediaList(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber, + nsMediaList* aMediaList, + bool aHTMLMode) +{ + static_cast(mImpl)-> + ParseMediaList(aBuffer, aURI, aLineNumber, aMediaList, aHTMLMode); +} + +bool +nsCSSParser::ParseSourceSizeList(const nsAString& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber, + InfallibleTArray< nsAutoPtr >& aQueries, + InfallibleTArray& aValues, + bool aHTMLMode) +{ + return static_cast(mImpl)-> + ParseSourceSizeList(aBuffer, aURI, aLineNumber, aQueries, aValues, + aHTMLMode); +} + +bool +nsCSSParser::ParseFontFamilyListString(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber, + nsCSSValue& aValue) +{ + return static_cast(mImpl)-> + ParseFontFamilyListString(aBuffer, aURI, aLineNumber, aValue); +} + +bool +nsCSSParser::ParseColorString(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber, + nsCSSValue& aValue, + bool aSuppressErrors /* false */) +{ + return static_cast(mImpl)-> + ParseColorString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors); +} + +bool +nsCSSParser::ParseMarginString(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber, + nsCSSValue& aValue, + bool aSuppressErrors /* false */) +{ + return static_cast(mImpl)-> + ParseMarginString(aBuffer, aURI, aLineNumber, aValue, aSuppressErrors); +} + +nsresult +nsCSSParser::ParseSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURI, + uint32_t aLineNumber, + nsCSSSelectorList** aSelectorList) +{ + return static_cast(mImpl)-> + ParseSelectorString(aSelectorString, aURI, aLineNumber, aSelectorList); +} + +already_AddRefed +nsCSSParser::ParseKeyframeRule(const nsSubstring& aBuffer, + nsIURI* aURI, + uint32_t aLineNumber) +{ + return static_cast(mImpl)-> + ParseKeyframeRule(aBuffer, aURI, aLineNumber); +} + +bool +nsCSSParser::ParseKeyframeSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURI, + uint32_t aLineNumber, + InfallibleTArray& aSelectorList) +{ + return static_cast(mImpl)-> + ParseKeyframeSelectorString(aSelectorString, aURI, aLineNumber, + aSelectorList); +} + +bool +nsCSSParser::EvaluateSupportsDeclaration(const nsAString& aProperty, + const nsAString& aValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal) +{ + return static_cast(mImpl)-> + EvaluateSupportsDeclaration(aProperty, aValue, aDocURL, aBaseURL, + aDocPrincipal); +} + +bool +nsCSSParser::EvaluateSupportsCondition(const nsAString& aCondition, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal) +{ + return static_cast(mImpl)-> + EvaluateSupportsCondition(aCondition, aDocURL, aBaseURL, aDocPrincipal); +} + +bool +nsCSSParser::EnumerateVariableReferences(const nsAString& aPropertyValue, + VariableEnumFunc aFunc, + void* aData) +{ + return static_cast(mImpl)-> + EnumerateVariableReferences(aPropertyValue, aFunc, aData); +} + +bool +nsCSSParser::ResolveVariableValue(const nsAString& aPropertyValue, + const CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken) +{ + return static_cast(mImpl)-> + ResolveVariableValue(aPropertyValue, aVariables, + aResult, aFirstToken, aLastToken); +} + +void +nsCSSParser::ParsePropertyWithVariableReferences( + nsCSSPropertyID aPropertyID, + nsCSSPropertyID aShorthandPropertyID, + const nsAString& aValue, + const CSSVariableValues* aVariables, + nsRuleData* aRuleData, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal, + CSSStyleSheet* aSheet, + uint32_t aLineNumber, + uint32_t aLineOffset) +{ + static_cast(mImpl)-> + ParsePropertyWithVariableReferences(aPropertyID, aShorthandPropertyID, + aValue, aVariables, aRuleData, aDocURL, + aBaseURL, aDocPrincipal, aSheet, + aLineNumber, aLineOffset); +} + +bool +nsCSSParser::ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL, + nsAString& aName) +{ + return static_cast(mImpl)-> + ParseCounterStyleName(aBuffer, aURL, aName); +} + +bool +nsCSSParser::ParseCounterDescriptor(nsCSSCounterDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + return static_cast(mImpl)-> + ParseCounterDescriptor(aDescID, aBuffer, + aSheetURL, aBaseURL, aSheetPrincipal, aValue); +} + +bool +nsCSSParser::ParseFontFaceDescriptor(nsCSSFontDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + return static_cast(mImpl)-> + ParseFontFaceDescriptor(aDescID, aBuffer, + aSheetURL, aBaseURL, aSheetPrincipal, aValue); +} + +bool +nsCSSParser::IsValueValidForProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue) +{ + return static_cast(mImpl)-> + IsValueValidForProperty(aPropID, aPropValue); +} + +/* static */ +uint8_t +nsCSSParser::ControlCharVisibilityDefault() +{ + return sControlCharVisibility + ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE + : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN; +} diff --git a/layout/style/nsCSSParser.h b/layout/style/nsCSSParser.h new file mode 100644 index 000000000..37cd325f2 --- /dev/null +++ b/layout/style/nsCSSParser.h @@ -0,0 +1,346 @@ +/* -*- 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/. */ + +/* parsing of CSS stylesheets, based on a token stream from the CSS scanner */ + +#ifndef nsCSSParser_h___ +#define nsCSSParser_h___ + +#include "mozilla/Attributes.h" +#include "mozilla/css/Loader.h" + +#include "nsCSSPropertyID.h" +#include "nsCSSScanner.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsStringFwd.h" +#include "nsTArrayForwardDeclare.h" + +class nsIPrincipal; +class nsIURI; +struct nsCSSSelectorList; +class nsMediaList; +class nsMediaQuery; +class nsCSSKeyframeRule; +class nsCSSValue; +struct nsRuleData; + +namespace mozilla { +class CSSStyleSheet; +class CSSVariableValues; +namespace css { +class Rule; +class Declaration; +class StyleRule; +} // namespace css +} // namespace mozilla + +// Interface to the css parser. + +class MOZ_STACK_CLASS nsCSSParser { +public: + explicit nsCSSParser(mozilla::css::Loader* aLoader = nullptr, + mozilla::CSSStyleSheet* aSheet = nullptr); + ~nsCSSParser(); + + static void Startup(); + static void Shutdown(); + +private: + nsCSSParser(nsCSSParser const&) = delete; + nsCSSParser& operator=(nsCSSParser const&) = delete; + +public: + /** + * Parse aInput into the stylesheet that was previously passed to the + * constructor. Calling this method on an nsCSSParser that had nullptr + * passed in as the style sheet is an error. + * + * @param aInput the data to parse + * @param aSheetURL the URI to use as the sheet URI (for error reporting). + * This must match the URI of the sheet passed to + * the constructor. + * @param aBaseURI the URI to use for relative URI resolution + * @param aSheetPrincipal the principal of the stylesheet. This must match + * the principal of the sheet passed to the + * constructor. + * @param aLineNumber the line number of the first line of the sheet. + * @param aReusableSheets style sheets that can be reused by an @import. + * This can be nullptr. + */ + nsresult ParseSheet(const nsAString& aInput, + nsIURI* aSheetURL, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + uint32_t aLineNumber, + mozilla::css::LoaderReusableStyleSheets* aReusableSheets = + nullptr); + + // Parse HTML style attribute or its equivalent in other markup + // languages. aBaseURL is the base url to use for relative links in + // the declaration. + already_AddRefed + ParseStyleAttribute(const nsAString& aAttributeValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aNodePrincipal); + + // Parse the body of a declaration block. Very similar to + // ParseStyleAttribute, but used under different circumstances. + // The contents of aDeclaration will be erased and replaced with the + // results of parsing; aChanged will be set true if the aDeclaration + // argument was modified. + nsresult ParseDeclarations(const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + mozilla::css::Declaration* aDeclaration, + bool* aChanged); + + nsresult ParseRule(const nsAString& aRule, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + mozilla::css::Rule** aResult); + + // Parse the value of a single CSS property, and add or replace that + // property in aDeclaration. + // + // SVG "mapped attributes" (which correspond directly to CSS + // properties) are parsed slightly differently from regular CSS; in + // particular, units may be omitted from . The 'aIsSVGMode' + // argument controls this quirk. Note that this *only* applies to + // mapped attributes, not inline styles or full style sheets in SVG. + void ParseProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + mozilla::css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant, + bool aIsSVGMode = false); + + // Same as ParseProperty but returns an nsCSSValue in aResult + // rather than storing the property in a Declaration. aPropID + // must be a longhand property. + void ParseLonghandProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aResult); + + // Parse the value of a CSS transform property. Returns + // whether the value was successfully parsed. If + // aDisallowRelativeValues is true then this method will + // only successfully parse if all values are numbers or + // have non-relative dimensions. + bool ParseTransformProperty(const nsAString& aPropValue, + bool aDisallowRelativeValues, + nsCSSValue& aResult); + + // The same as ParseProperty but for a variable. + void ParseVariable(const nsAString& aVariableName, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + mozilla::css::Declaration* aDeclaration, + bool* aChanged, + bool aIsImportant); + /** + * Parse aBuffer into a media list |aMediaList|, which must be + * non-null, replacing its current contents. If aHTMLMode is true, + * parse according to HTML rules, with commas as the most important + * delimiter. Otherwise, parse according to CSS rules, with + * parentheses and strings more important than commas. |aURL| and + * |aLineNumber| are used for error reporting. + */ + void ParseMediaList(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber, + nsMediaList* aMediaList, + bool aHTMLMode); + + /* + * Parse aBuffer into a list of media queries and their associated values, + * according to grammar: + * = #? + * = ? + * + * Note that this grammar is top-level: The function expects to consume the + * entire input buffer. + * + * Output arrays overwritten (not appended) and are cleared in case of parse + * failure. + */ + bool ParseSourceSizeList(const nsAString& aBuffer, + nsIURI* aURI, // for error reporting + uint32_t aLineNumber, // for error reporting + InfallibleTArray< nsAutoPtr >& aQueries, + InfallibleTArray& aValues, + bool aHTMLMode); + + /** + * Parse aBuffer into a nsCSSValue |aValue|. Will return false + * if aBuffer is not a valid font family list. + */ + bool ParseFontFamilyListString(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber, + nsCSSValue& aValue); + + /** + * Parse aBuffer into a nsCSSValue |aValue|. Will return false + * if aBuffer is not a valid CSS color specification. + * One can use nsRuleNode::ComputeColor to compute an nscolor from + * the returned nsCSSValue. + */ + bool ParseColorString(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber, + nsCSSValue& aValue, + bool aSuppressErrors = false); + + /** + * Parse aBuffer into a nsCSSValue |aValue|. Will return false + * if aBuffer is not a valid CSS margin specification. + * One can use nsRuleNode::GetRectValue to compute an nsCSSRect from + * the returned nsCSSValue. + */ + bool ParseMarginString(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber, + nsCSSValue& aValue, + bool aSuppressErrors = false); + + /** + * Parse aBuffer into a selector list. On success, caller must + * delete *aSelectorList when done with it. + */ + nsresult ParseSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURL, + uint32_t aLineNumber, + nsCSSSelectorList** aSelectorList); + + /* + * Parse a keyframe rule (which goes inside an @keyframes rule). + * Return it if the parse was successful. + */ + already_AddRefed + ParseKeyframeRule(const nsSubstring& aBuffer, + nsIURI* aURL, + uint32_t aLineNumber); + + /* + * Parse a selector list for a keyframe rule. Return whether + * the parse succeeded. + */ + bool ParseKeyframeSelectorString(const nsSubstring& aSelectorString, + nsIURI* aURL, + uint32_t aLineNumber, + InfallibleTArray& aSelectorList); + + /** + * Parse a property and value and return whether the property/value pair + * is supported. + */ + bool EvaluateSupportsDeclaration(const nsAString& aProperty, + const nsAString& aValue, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal); + + /** + * Parse an @supports condition and returns the result of evaluating the + * condition. + */ + bool EvaluateSupportsCondition(const nsAString& aCondition, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal); + + typedef void (*VariableEnumFunc)(const nsAString&, void*); + + /** + * Parses aPropertyValue as a property value and calls aFunc for each + * variable reference that is found. Returns false if there was + * a syntax error in the use of variable references. + */ + bool EnumerateVariableReferences(const nsAString& aPropertyValue, + VariableEnumFunc aFunc, + void* aData); + + /** + * Parses aPropertyValue as a property value and resolves variable references + * using the values in aVariables. + */ + bool ResolveVariableValue(const nsAString& aPropertyValue, + const mozilla::CSSVariableValues* aVariables, + nsString& aResult, + nsCSSTokenSerializationType& aFirstToken, + nsCSSTokenSerializationType& aLastToken); + + /** + * Parses a string as a CSS token stream value for particular property, + * resolving any variable references. The parsed property value is stored + * in the specified nsRuleData object. If aShorthandPropertyID has a value + * other than eCSSProperty_UNKNOWN, this is the property that will be parsed; + * otherwise, aPropertyID will be parsed. Either way, only aPropertyID, + * a longhand property, will be copied over to the rule data. + * + * If the property cannot be parsed, it will be treated as if 'initial' or + * 'inherit' were specified, for non-inherited and inherited properties + * respectively. + */ + void ParsePropertyWithVariableReferences( + nsCSSPropertyID aPropertyID, + nsCSSPropertyID aShorthandPropertyID, + const nsAString& aValue, + const mozilla::CSSVariableValues* aVariables, + nsRuleData* aRuleData, + nsIURI* aDocURL, + nsIURI* aBaseURL, + nsIPrincipal* aDocPrincipal, + mozilla::CSSStyleSheet* aSheet, + uint32_t aLineNumber, + uint32_t aLineOffset); + + bool ParseCounterStyleName(const nsAString& aBuffer, + nsIURI* aURL, + nsAString& aName); + + bool ParseCounterDescriptor(nsCSSCounterDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); + + bool ParseFontFaceDescriptor(nsCSSFontDesc aDescID, + const nsAString& aBuffer, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); + + // Check whether a given value can be applied to a property. + bool IsValueValidForProperty(const nsCSSPropertyID aPropID, + const nsAString& aPropValue); + + // Return the default value to be used for -moz-control-character-visibility, + // from preferences (cached by our Startup(), so that both nsStyleText and + // nsRuleNode can have fast access to it). + static uint8_t ControlCharVisibilityDefault(); + +protected: + // This is a CSSParserImpl*, but if we expose that type name in this + // header, we can't put the type definition (in nsCSSParser.cpp) in + // the anonymous namespace. + void* mImpl; +}; + +#endif /* nsCSSParser_h___ */ diff --git a/layout/style/nsCSSPropAliasList.h b/layout/style/nsCSSPropAliasList.h new file mode 100644 index 000000000..2699549ff --- /dev/null +++ b/layout/style/nsCSSPropAliasList.h @@ -0,0 +1,498 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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 CSS property aliases with data about them, for preprocessing + */ + +/****** + + This file contains the list of all CSS properties that are just + aliases for other properties (e.g., for when we temporarily continue + to support a prefixed property after adding support for its unprefixed + form). It is designed to be used as inline input through the magic of + C preprocessing. All entries must be enclosed in the appropriate + CSS_PROP_ALIAS macro which will have cruel and unusual things done to + it. + + The arguments to CSS_PROP_ALIAS are: + + -. 'aliasname' entries represent a CSS property name and *must* use + only lowercase characters. + + -. 'id' should be the same as the 'id' field in nsCSSPropList.h for + the property that 'aliasname' is being aliased to. + + -. 'method' is the CSS2Properties property name. Unlike + nsCSSPropList.h, prefixes should just be included in this file (rather + than needing the CSS_PROP_DOMPROP_PREFIXED(prop) macro). + + -. 'pref' is the name of a pref that controls whether the property + is enabled. The property is enabled if 'pref' is an empty string, + or if the boolean property whose name is 'pref' is set to true. + + ******/ + +CSS_PROP_ALIAS(word-wrap, + overflow_wrap, + WordWrap, + "") +CSS_PROP_ALIAS(-moz-transform-origin, + transform_origin, + MozTransformOrigin, + "layout.css.prefixes.transforms") +CSS_PROP_ALIAS(-moz-perspective-origin, + perspective_origin, + MozPerspectiveOrigin, + "layout.css.prefixes.transforms") +CSS_PROP_ALIAS(-moz-perspective, + perspective, + MozPerspective, + "layout.css.prefixes.transforms") +CSS_PROP_ALIAS(-moz-transform-style, + transform_style, + MozTransformStyle, + "layout.css.prefixes.transforms") +CSS_PROP_ALIAS(-moz-backface-visibility, + backface_visibility, + MozBackfaceVisibility, + "layout.css.prefixes.transforms") +CSS_PROP_ALIAS(-moz-border-image, + border_image, + MozBorderImage, + "layout.css.prefixes.border-image") +CSS_PROP_ALIAS(-moz-transition, + transition, + MozTransition, + "layout.css.prefixes.transitions") +CSS_PROP_ALIAS(-moz-transition-delay, + transition_delay, + MozTransitionDelay, + "layout.css.prefixes.transitions") +CSS_PROP_ALIAS(-moz-transition-duration, + transition_duration, + MozTransitionDuration, + "layout.css.prefixes.transitions") +CSS_PROP_ALIAS(-moz-transition-property, + transition_property, + MozTransitionProperty, + "layout.css.prefixes.transitions") +CSS_PROP_ALIAS(-moz-transition-timing-function, + transition_timing_function, + MozTransitionTimingFunction, + "layout.css.prefixes.transitions") +CSS_PROP_ALIAS(-moz-animation, + animation, + MozAnimation, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-delay, + animation_delay, + MozAnimationDelay, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-direction, + animation_direction, + MozAnimationDirection, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-duration, + animation_duration, + MozAnimationDuration, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-fill-mode, + animation_fill_mode, + MozAnimationFillMode, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-iteration-count, + animation_iteration_count, + MozAnimationIterationCount, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-name, + animation_name, + MozAnimationName, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-play-state, + animation_play_state, + MozAnimationPlayState, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-animation-timing-function, + animation_timing_function, + MozAnimationTimingFunction, + "layout.css.prefixes.animations") +CSS_PROP_ALIAS(-moz-box-sizing, + box_sizing, + MozBoxSizing, + "layout.css.prefixes.box-sizing") +CSS_PROP_ALIAS(-moz-font-feature-settings, + font_feature_settings, + MozFontFeatureSettings, + "layout.css.prefixes.font-features") +CSS_PROP_ALIAS(-moz-font-language-override, + font_language_override, + MozFontLanguageOverride, + "layout.css.prefixes.font-features") +CSS_PROP_ALIAS(-moz-padding-end, + padding_inline_end, + MozPaddingEnd, + "") +CSS_PROP_ALIAS(-moz-padding-start, + padding_inline_start, + MozPaddingStart, + "") +CSS_PROP_ALIAS(-moz-margin-end, + margin_inline_end, + MozMarginEnd, + "") +CSS_PROP_ALIAS(-moz-margin-start, + margin_inline_start, + MozMarginStart, + "") +CSS_PROP_ALIAS(-moz-border-end, + border_inline_end, + MozBorderEnd, + "") +CSS_PROP_ALIAS(-moz-border-end-color, + border_inline_end_color, + MozBorderEndColor, + "") +CSS_PROP_ALIAS(-moz-border-end-style, + border_inline_end_style, + MozBorderEndStyle, + "") +CSS_PROP_ALIAS(-moz-border-end-width, + border_inline_end_width, + MozBorderEndWidth, + "") +CSS_PROP_ALIAS(-moz-border-start, + border_inline_start, + MozBorderStart, + "") +CSS_PROP_ALIAS(-moz-border-start-color, + border_inline_start_color, + MozBorderStartColor, + "") +CSS_PROP_ALIAS(-moz-border-start-style, + border_inline_start_style, + MozBorderStartStyle, + "") +CSS_PROP_ALIAS(-moz-border-start-width, + border_inline_start_width, + MozBorderStartWidth, + "") +CSS_PROP_ALIAS(-moz-hyphens, + hyphens, + MozHyphens, + "") +CSS_PROP_ALIAS(-moz-text-align-last, + text_align_last, + MozTextAlignLast, + "") +CSS_PROP_ALIAS(-moz-column-count, + column_count, + MozColumnCount, + "") +CSS_PROP_ALIAS(-moz-column-fill, + column_fill, + MozColumnFill, + "") +CSS_PROP_ALIAS(-moz-column-gap, + column_gap, + MozColumnGap, + "") +CSS_PROP_ALIAS(-moz-column-rule, + column_rule, + MozColumnRule, + "") +CSS_PROP_ALIAS(-moz-column-rule-color, + column_rule_color, + MozColumnRuleColor, + "") +CSS_PROP_ALIAS(-moz-column-rule-style, + column_rule_style, + MozColumnRuleStyle, + "") +CSS_PROP_ALIAS(-moz-column-rule-width, + column_rule_width, + MozColumnRuleWidth, + "") +CSS_PROP_ALIAS(-moz-column-width, + column_width, + MozColumnWidth, + "") +CSS_PROP_ALIAS(-moz-columns, + columns, + MozColumns, + "") + +#define WEBKIT_PREFIX_PREF "layout.css.prefixes.webkit" + +// -webkit- prefixes +CSS_PROP_ALIAS(-webkit-animation, + animation, + WebkitAnimation, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-delay, + animation_delay, + WebkitAnimationDelay, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-direction, + animation_direction, + WebkitAnimationDirection, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-duration, + animation_duration, + WebkitAnimationDuration, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-fill-mode, + animation_fill_mode, + WebkitAnimationFillMode, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-iteration-count, + animation_iteration_count, + WebkitAnimationIterationCount, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-name, + animation_name, + WebkitAnimationName, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-play-state, + animation_play_state, + WebkitAnimationPlayState, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-animation-timing-function, + animation_timing_function, + WebkitAnimationTimingFunction, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-filter, + filter, + WebkitFilter, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-text-size-adjust, + text_size_adjust, + WebkitTextSizeAdjust, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-transform, + transform, + WebkitTransform, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transform-origin, + transform_origin, + WebkitTransformOrigin, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transform-style, + transform_style, + WebkitTransformStyle, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-backface-visibility, + backface_visibility, + WebkitBackfaceVisibility, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-perspective, + perspective, + WebkitPerspective, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-perspective-origin, + perspective_origin, + WebkitPerspectiveOrigin, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-transition, + transition, + WebkitTransition, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transition-delay, + transition_delay, + WebkitTransitionDelay, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transition-duration, + transition_duration, + WebkitTransitionDuration, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transition-property, + transition_property, + WebkitTransitionProperty, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-transition-timing-function, + transition_timing_function, + WebkitTransitionTimingFunction, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-border-radius, + border_radius, + WebkitBorderRadius, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-border-top-left-radius, + border_top_left_radius, + WebkitBorderTopLeftRadius, // really no dom property + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-border-top-right-radius, + border_top_right_radius, + WebkitBorderTopRightRadius, // really no dom property + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-border-bottom-left-radius, + border_bottom_left_radius, + WebkitBorderBottomLeftRadius, // really no dom property + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-border-bottom-right-radius, + border_bottom_right_radius, + WebkitBorderBottomRightRadius, // really no dom property + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-background-clip, + background_clip, + WebkitBackgroundClip, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-background-origin, + background_origin, + WebkitBackgroundOrigin, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-background-size, + background_size, + WebkitBackgroundSize, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-border-image, + border_image, + WebkitBorderImage, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-box-shadow, + box_shadow, + WebkitBoxShadow, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-sizing, + box_sizing, + WebkitBoxSizing, + WEBKIT_PREFIX_PREF) + +// Alias -webkit-box properties to their -moz-box equivalents. +// (NOTE: Even though they're aliases, in practice these -webkit properties +// will behave a bit differently from their -moz versions, if they're +// accompanied by "display:-webkit-box", because we generate a different frame +// for those two display values.) +CSS_PROP_ALIAS(-webkit-box-flex, + box_flex, + WebkitBoxFlex, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-ordinal-group, + box_ordinal_group, + WebkitBoxOrdinalGroup, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-orient, + box_orient, + WebkitBoxOrient, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-direction, + box_direction, + WebkitBoxDirection, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-align, + box_align, + WebkitBoxAlign, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-box-pack, + box_pack, + WebkitBoxPack, + WEBKIT_PREFIX_PREF) + +// Alias -webkit-flex related properties to their unprefixed equivalents: +// (Matching ordering at https://drafts.csswg.org/css-flexbox-1/#property-index ) +CSS_PROP_ALIAS(-webkit-flex-direction, + flex_direction, + WebkitFlexDirection, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex-wrap, + flex_wrap, + WebkitFlexWrap, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex-flow, + flex_flow, + WebkitFlexFlow, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-order, + order, + WebkitOrder, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex, + flex, + WebkitFlex, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex-grow, + flex_grow, + WebkitFlexGrow, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex-shrink, + flex_shrink, + WebkitFlexShrink, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-flex-basis, + flex_basis, + WebkitFlexBasis, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-justify-content, + justify_content, + WebkitJustifyContent, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-align-items, + align_items, + WebkitAlignItems, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-align-self, + align_self, + WebkitAlignSelf, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-align-content, + align_content, + WebkitAlignContent, + WEBKIT_PREFIX_PREF) + +CSS_PROP_ALIAS(-webkit-user-select, + user_select, + WebkitUserSelect, + WEBKIT_PREFIX_PREF) + +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND +CSS_PROP_ALIAS(-webkit-mask, + mask, + WebkitMask, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-clip, + mask_clip, + WebkitMaskClip, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-composite, + mask_composite, + WebkitMaskComposite, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-image, + mask_image, + WebkitMaskImage, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-origin, + mask_origin, + WebkitMaskOrigin, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-position, + mask_position, + WebkitMaskPosition, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-position-x, + mask_position_x, + WebkitMaskPositionX, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-position-y, + mask_position_y, + WebkitMaskPositionY, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-repeat, + mask_repeat, + WebkitMaskRepeat, + WEBKIT_PREFIX_PREF) +CSS_PROP_ALIAS(-webkit-mask-size, + mask_size, + WebkitMaskSize, + WEBKIT_PREFIX_PREF) +#endif +#undef WEBKIT_PREFIX_PREF diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h new file mode 100644 index 000000000..6931d8c2b --- /dev/null +++ b/layout/style/nsCSSPropList.h @@ -0,0 +1,4635 @@ +/* -*- 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/. */ + +/* + * a list of all CSS properties with considerable data about them, for + * preprocessing + */ + +/****** + + This file contains the list of all parsed CSS properties. It is + designed to be used as inline input through the magic of C + preprocessing. All entries must be enclosed in the appropriate + CSS_PROP_* macro which will have cruel and unusual things done to it. + It is recommended (but not strictly necessary) to keep all entries in + alphabetical order. + + The arguments to CSS_PROP, CSS_PROP_LOGICAL and CSS_PROP_* are: + + -. 'name' entries represent a CSS property name and *must* use only + lowercase characters. + + -. 'id' should be the same as 'name' except that all hyphens ('-') + in 'name' are converted to underscores ('_') in 'id'. For properties + on a standards track, any '-moz-' prefix is removed in 'id'. This + lets us do nice things with the macros without having to copy/convert + strings at runtime. These are the names used for the enum values of + the nsCSSPropertyID enumeration defined in nsCSSProps.h. + + -. 'method' is designed to be as input for CSS2Properties and similar + callers. It must always be the same as 'name' except it must use + InterCaps and all hyphens ('-') must be removed. Callers using this + parameter must also define the CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, + privatename_) macro to yield either publicname_ or privatename_. + The names differ in that publicname_ has Moz prefixes where they are + used, and also in CssFloat vs. Float. The caller's choice depends on + whether the use is for internal use such as eCSSProperty_* or + nsRuleData::ValueFor* or external use such as exposing DOM properties. + + -. 'flags', a bitfield containing CSS_PROPERTY_* flags. + + -. 'pref' is the name of a pref that controls whether the property + is enabled. The property is enabled if 'pref' is an empty string, + or if the boolean property whose name is 'pref' is set to true. + + -. 'parsevariant', to be passed to ParseVariant in the parser. + + -. 'kwtable', which is either nullptr or the name of the appropriate + keyword table member of class nsCSSProps, for use in + nsCSSProps::LookupPropertyValue. + + -. 'group_' [used only for CSS_PROP_LOGICAL] is the name of + the logical property group that contains the physical properties + that can be set by this logical property. The name must be one + from nsCSSPropLogicalGroupList.h. For example, this would be + 'BorderColor' for 'border-block-start-color'. + + -. 'stylestruct_' [used only for CSS_PROP and CSS_PROP_LOGICAL, not + CSS_PROP_*] gives the name of the style struct. Can be used to make + nsStyle##stylestruct_ and eStyleStruct_##stylestruct_ + + -. 'stylestructoffset_' gives the result of offsetof(nsStyle*, + member). Ignored (and generally CSS_PROP_NO_OFFSET, or -1) for + properties whose animtype_ is eStyleAnimType_None. + + -. 'animtype_' gives the animation type (see nsStyleAnimType) of this + property. + + CSS_PROP_SHORTHAND only takes 1-5. + + CSS_PROP_LOGICAL should be used instead of CSS_PROP_struct when + defining logical properties (which also must be defined with the + CSS_PROPERTY_LOGICAL flag). Logical shorthand properties should still + be defined with CSS_PROP_SHORTHAND. + + ******/ + + +/*************************************************************************/ + + +// All includers must explicitly define CSS_PROP_SHORTHAND if they +// want it. +#ifndef CSS_PROP_SHORTHAND +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) /* nothing */ +#define DEFINED_CSS_PROP_SHORTHAND +#endif + +#define CSS_PROP_DOMPROP_PREFIXED(name_) \ + CSS_PROP_PUBLIC_OR_PRIVATE(Moz ## name_, name_) + +#define CSS_PROP_NO_OFFSET (-1) + +// Callers may define CSS_PROP_LIST_EXCLUDE_INTERNAL if they want to +// exclude internal properties that are not represented in the DOM (only +// the DOM style code defines this). All properties defined in an +// #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL section must have the +// CSS_PROPERTY_INTERNAL flag set. + +// When capturing all properties by defining CSS_PROP, callers must also +// define one of the following three macros: +// +// CSS_PROP_LIST_EXCLUDE_LOGICAL +// Does not include logical properties (defined with CSS_PROP_LOGICAL, +// such as margin-inline-start) when capturing properties to CSS_PROP. +// +// CSS_PROP_LIST_INCLUDE_LOGICAL +// Does include logical properties when capturing properties to +// CSS_PROP. +// +// CSS_PROP_LOGICAL +// Captures logical properties separately to CSS_PROP_LOGICAL. +// +// (CSS_PROP_LIST_EXCLUDE_LOGICAL is used for example to ensure +// gPropertyCountInStruct and gPropertyIndexInStruct do not allocate any +// storage to logical properties, since the result of the cascade, stored +// in an nsRuleData, does not need to store both logical and physical +// property values.) + +// Callers may also define CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +// to exclude properties that are not considered to be components of the 'all' +// shorthand property. Currently this excludes 'direction' and 'unicode-bidi', +// as required by the CSS Cascading and Inheritance specification, and any +// internal properties that cannot be changed by using CSS syntax. For example, +// the internal '-moz-system-font' property is not excluded, as it is set by the +// 'font' shorthand, while '-x-lang' is excluded as there is no way to set this +// internal property from a style sheet. + +// A caller who wants all the properties can define the |CSS_PROP| +// macro. +#ifdef CSS_PROP + +#define USED_CSS_PROP +#define CSS_PROP_FONT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Font, stylestructoffset_, animtype_) +#define CSS_PROP_COLOR(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Color, stylestructoffset_, animtype_) +#define CSS_PROP_BACKGROUND(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Background, stylestructoffset_, animtype_) +#define CSS_PROP_LIST(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, List, stylestructoffset_, animtype_) +#define CSS_PROP_POSITION(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Position, stylestructoffset_, animtype_) +#define CSS_PROP_TEXT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Text, stylestructoffset_, animtype_) +#define CSS_PROP_TEXTRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, TextReset, stylestructoffset_, animtype_) +#define CSS_PROP_DISPLAY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Display, stylestructoffset_, animtype_) +#define CSS_PROP_VISIBILITY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Visibility, stylestructoffset_, animtype_) +#define CSS_PROP_CONTENT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Content, stylestructoffset_, animtype_) +#define CSS_PROP_USERINTERFACE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, UserInterface, stylestructoffset_, animtype_) +#define CSS_PROP_UIRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, UIReset, stylestructoffset_, animtype_) +#define CSS_PROP_TABLE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Table, stylestructoffset_, animtype_) +#define CSS_PROP_TABLEBORDER(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, TableBorder, stylestructoffset_, animtype_) +#define CSS_PROP_MARGIN(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Margin, stylestructoffset_, animtype_) +#define CSS_PROP_PADDING(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Padding, stylestructoffset_, animtype_) +#define CSS_PROP_BORDER(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Border, stylestructoffset_, animtype_) +#define CSS_PROP_OUTLINE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Outline, stylestructoffset_, animtype_) +#define CSS_PROP_XUL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, XUL, stylestructoffset_, animtype_) +#define CSS_PROP_COLUMN(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Column, stylestructoffset_, animtype_) +#define CSS_PROP_SVG(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, SVG, stylestructoffset_, animtype_) +#define CSS_PROP_SVGRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, SVGReset, stylestructoffset_, animtype_) +#define CSS_PROP_VARIABLES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Variables, stylestructoffset_, animtype_) +#define CSS_PROP_EFFECTS(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Effects, stylestructoffset_, animtype_) + +// And similarly for logical properties. An includer can define +// CSS_PROP_LOGICAL to capture all logical properties, but otherwise they +// are included in CSS_PROP (as long as CSS_PROP_LIST_INCLUDE_LOGICAL is +// defined). +#if defined(CSS_PROP_LOGICAL) && defined(CSS_PROP_LIST_EXCLUDE_LOGICAL) || defined(CSS_PROP_LOGICAL) && defined(CSS_PROP_LIST_INCLUDE_LOGICAL) || defined(CSS_PROP_LIST_EXCLUDE_LOGICAL) && defined(CSS_PROP_LIST_INCLUDE_LOGICAL) +#error Do not define more than one of CSS_PROP_LOGICAL, CSS_PROP_LIST_EXCLUDE_LOGICAL and CSS_PROP_LIST_INCLUDE_LOGICAL when capturing properties using CSS_PROP. +#endif + +#ifndef CSS_PROP_LOGICAL +#ifdef CSS_PROP_LIST_INCLUDE_LOGICAL +#define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, group_, struct_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, struct_, stylestructoffset_, animtype_) +#else +#ifndef CSS_PROP_LIST_EXCLUDE_LOGICAL +#error Must define exactly one of CSS_PROP_LOGICAL, CSS_PROP_LIST_EXCLUDE_LOGICAL and CSS_PROP_LIST_INCLUDE_LOGICAL when capturing properties using CSS_PROP. +#endif +#define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, group_, struct_, stylestructoffset_, animtype_) /* nothing */ +#endif +#define DEFINED_CSS_PROP_LOGICAL +#endif + +#else /* !defined(CSS_PROP) */ + +// An includer who does not define CSS_PROP can define any or all of the +// per-struct macros that are equivalent to it, and the rest will be +// ignored. + +#if defined(CSS_PROP_LIST_EXCLUDE_LOGICAL) || defined(CSS_PROP_LIST_INCLUDE_LOGICAL) +#error Do not define CSS_PROP_LIST_EXCLUDE_LOGICAL or CSS_PROP_LIST_INCLUDE_LOGICAL when not capturing properties using CSS_PROP. +#endif + +#ifndef CSS_PROP_FONT +#define CSS_PROP_FONT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_FONT +#endif +#ifndef CSS_PROP_COLOR +#define CSS_PROP_COLOR(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_COLOR +#endif +#ifndef CSS_PROP_BACKGROUND +#define CSS_PROP_BACKGROUND(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_BACKGROUND +#endif +#ifndef CSS_PROP_LIST +#define CSS_PROP_LIST(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_LIST +#endif +#ifndef CSS_PROP_POSITION +#define CSS_PROP_POSITION(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_POSITION +#endif +#ifndef CSS_PROP_TEXT +#define CSS_PROP_TEXT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_TEXT +#endif +#ifndef CSS_PROP_TEXTRESET +#define CSS_PROP_TEXTRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_TEXTRESET +#endif +#ifndef CSS_PROP_DISPLAY +#define CSS_PROP_DISPLAY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_DISPLAY +#endif +#ifndef CSS_PROP_VISIBILITY +#define CSS_PROP_VISIBILITY(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_VISIBILITY +#endif +#ifndef CSS_PROP_CONTENT +#define CSS_PROP_CONTENT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_CONTENT +#endif +#ifndef CSS_PROP_USERINTERFACE +#define CSS_PROP_USERINTERFACE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_USERINTERFACE +#endif +#ifndef CSS_PROP_UIRESET +#define CSS_PROP_UIRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_UIRESET +#endif +#ifndef CSS_PROP_TABLE +#define CSS_PROP_TABLE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_TABLE +#endif +#ifndef CSS_PROP_TABLEBORDER +#define CSS_PROP_TABLEBORDER(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_TABLEBORDER +#endif +#ifndef CSS_PROP_MARGIN +#define CSS_PROP_MARGIN(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_MARGIN +#endif +#ifndef CSS_PROP_PADDING +#define CSS_PROP_PADDING(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_PADDING +#endif +#ifndef CSS_PROP_BORDER +#define CSS_PROP_BORDER(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_BORDER +#endif +#ifndef CSS_PROP_OUTLINE +#define CSS_PROP_OUTLINE(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_OUTLINE +#endif +#ifndef CSS_PROP_XUL +#define CSS_PROP_XUL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_XUL +#endif +#ifndef CSS_PROP_COLUMN +#define CSS_PROP_COLUMN(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_COLUMN +#endif +#ifndef CSS_PROP_SVG +#define CSS_PROP_SVG(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_SVG +#endif +#ifndef CSS_PROP_SVGRESET +#define CSS_PROP_SVGRESET(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_SVGRESET +#endif +#ifndef CSS_PROP_VARIABLES +#define CSS_PROP_VARIABLES(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_VARIABLES +#endif +#ifndef CSS_PROP_EFFECTS +#define CSS_PROP_EFFECTS(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_EFFECTS +#endif + +#ifndef CSS_PROP_LOGICAL +#define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, group_, struct_, stylestructoffset_, animtype_) /* nothing */ +#define DEFINED_CSS_PROP_LOGICAL +#endif + +#endif /* !defined(CSS_PROP) */ + +/*************************************************************************/ + +// For notes XXX bug 3935 below, the names being parsed do not correspond +// to the constants used internally. It would be nice to bring the +// constants into line sometime. + +// The parser will refuse to parse properties marked with -x-. + +// Those marked XXX bug 48973 are CSS2 properties that we support +// differently from the spec for UI requirements. If we ever +// support them correctly the old constants need to be renamed and +// new ones should be entered. + +// CSS2.1 section 5.12.1 says that the properties that apply to +// :first-line are: font properties, color properties, background +// properties, 'word-spacing', 'letter-spacing', 'text-decoration', +// 'vertical-align', 'text-transform', and 'line-height'. +// +// We also allow 'text-shadow', which was listed in CSS2 (where the +// property existed). + +// CSS2.1 section 5.12.2 says that the properties that apply to +// :first-letter are: font properties, 'text-decoration', +// 'text-transform', 'letter-spacing', 'word-spacing' (when +// appropriate), 'line-height', 'float', 'vertical-align' (only if +// 'float' is 'none'), margin properties, padding properties, border +// properties, 'color', and background properties. We also allow +// 'text-shadow' (see above) and 'box-shadow' (which is like the +// border properties). + +// Please keep these sorted by property name, ignoring any "-moz-", +// "-webkit-" or "-x-" prefix. + +CSS_PROP_POSITION( + align-content, + align_content, + AlignContent, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + kAutoCompletionAlignJustifyContent, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + align-items, + align_items, + AlignItems, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + kAutoCompletionAlignItems, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + align-self, + align_self, + AlignSelf, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + kAutoCompletionAlignJustifySelf, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + all, + all, + All, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.all-shorthand.enabled") +CSS_PROP_SHORTHAND( + animation, + animation, + Animation, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_DISPLAY( + animation-delay, + animation_delay, + AnimationDelay, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_TIME, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-direction, + animation_direction, + AnimationDirection, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kAnimationDirectionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-duration, + animation_duration, + AnimationDuration, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_TIME | VARIANT_NONNEGATIVE_DIMENSION, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-fill-mode, + animation_fill_mode, + AnimationFillMode, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kAnimationFillModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-iteration-count, + animation_iteration_count, + AnimationIterationCount, + CSS_PROPERTY_PARSE_VALUE_LIST | + // nonnegative per + // http://lists.w3.org/Archives/Public/www-style/2011Mar/0355.html + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD | VARIANT_NUMBER, // used by list parsing + kAnimationIterationCountKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-name, + animation_name, + AnimationName, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + // FIXME: The spec should say something about 'inherit' and 'initial' + // not being allowed. + VARIANT_NONE | VARIANT_IDENTIFIER_NO_INHERIT, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-play-state, + animation_play_state, + AnimationPlayState, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kAnimationPlayStateKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + animation-timing-function, + animation_timing_function, + AnimationTimingFunction, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing + kTransitionTimingFunctionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + -moz-appearance, + appearance, + CSS_PROP_DOMPROP_PREFIXED(Appearance), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kAppearanceKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + backface-visibility, + backface_visibility, + BackfaceVisibility, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBackfaceVisibilityKTable, + offsetof(nsStyleDisplay, mBackfaceVisibility), + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + background, + background, + Background, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BACKGROUND( + background-attachment, + background_attachment, + BackgroundAttachment, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerAttachmentKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BACKGROUND( + background-blend-mode, + background_blend_mode, + BackgroundBlendMode, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "layout.css.background-blend-mode.enabled", + VARIANT_KEYWORD, // used by list parsing + kBlendModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BACKGROUND( + background-clip, + background_clip, + BackgroundClip, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kBackgroundClipKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BACKGROUND( + background-color, + background_color, + BackgroundColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleBackground, mBackgroundColor), + eStyleAnimType_Color) +CSS_PROP_BACKGROUND( + background-image, + background_image, + BackgroundImage, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + VARIANT_IMAGE, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BACKGROUND( + background-origin, + background_origin, + BackgroundOrigin, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerOriginKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + background-position, + background_position, + BackgroundPosition, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "") +CSS_PROP_BACKGROUND( + background-position-x, + background_position_x, + BackgroundPositionX, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_BACKGROUND( + background-position-y, + background_position_y, + BackgroundPositionY, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_BACKGROUND( + background-repeat, + background_repeat, + BackgroundRepeat, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerRepeatKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BACKGROUND( + background-size, + background_size, + BackgroundSize, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerSizeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_DISPLAY( + -moz-binding, + binding, + CSS_PROP_DOMPROP_PREFIXED(Binding), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) // XXX bug 3935 +CSS_PROP_LOGICAL( + block-size, + block_size, + BlockSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Size, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SHORTHAND( + border, + border, + Border, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SHORTHAND( + border-block-end, + border_block_end, + BorderBlockEnd, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_LOGICAL( + border-block-end-color, + border_block_end_color, + BorderBlockEndColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HC, + nullptr, + BorderColor, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-block-end-style, + border_block_end_style, + BorderBlockEndStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HK, + kBorderStyleKTable, + BorderStyle, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-block-end-width, + border_block_end_width, + BorderBlockEndWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + BorderWidth, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SHORTHAND( + border-block-start, + border_block_start, + BorderBlockStart, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_LOGICAL( + border-block-start-color, + border_block_start_color, + BorderBlockStartColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_HC, + nullptr, + BorderColor, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-block-start-style, + border_block_start_style, + BorderBlockStartStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_HK, + kBorderStyleKTable, + BorderStyle, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-block-start-width, + border_block_start_width, + BorderBlockStartWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + BorderWidth, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SHORTHAND( + border-bottom, + border_bottom, + BorderBottom, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BORDER( + border-bottom-color, + border_bottom_color, + BorderBottomColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleBorder, mBorderBottomColor), + eStyleAnimType_ComplexColor) +CSS_PROP_BORDER( + -moz-border-bottom-colors, + border_bottom_colors, + CSS_PROP_DOMPROP_PREFIXED(BorderBottomColors), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-bottom-left-radius, + border_bottom_left_radius, + BorderBottomLeftRadius, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleBorder, mBorderRadius), + eStyleAnimType_Corner_BottomLeft) +CSS_PROP_BORDER( + border-bottom-right-radius, + border_bottom_right_radius, + BorderBottomRightRadius, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleBorder, mBorderRadius), + eStyleAnimType_Corner_BottomRight) +CSS_PROP_BORDER( + border-bottom-style, + border_bottom_style, + BorderBottomStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HK, + kBorderStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // on/off will need reflow +CSS_PROP_BORDER( + border-bottom-width, + border_bottom_width, + BorderBottomWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_TABLEBORDER( + border-collapse, + border_collapse, + BorderCollapse, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBorderCollapseKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + border-color, + border_color, + BorderColor, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK, + "") +CSS_PROP_SHORTHAND( + border-image, + border_image, + BorderImage, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BORDER( + border-image-outset, + border_image_outset, + BorderImageOutset, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-image-repeat, + border_image_repeat, + BorderImageRepeat, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + 0, + kBorderImageRepeatKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-image-slice, + border_image_slice, + BorderImageSlice, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + 0, + kBorderImageSliceKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-image-source, + border_image_source, + BorderImageSource, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + VARIANT_IMAGE | VARIANT_INHERIT, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-image-width, + border_image_width, + BorderImageWidth, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + border-inline-end, + border_inline_end, + BorderInlineEnd, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_LOGICAL( + border-inline-end-color, + border_inline_end_color, + BorderInlineEndColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HC, + nullptr, + BorderColor, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-inline-end-style, + border_inline_end_style, + BorderInlineEndStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HK, + kBorderStyleKTable, + BorderStyle, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-inline-end-width, + border_inline_end_width, + BorderInlineEndWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + BorderWidth, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SHORTHAND( + border-inline-start, + border_inline_start, + BorderInlineStart, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_LOGICAL( + border-inline-start-color, + border_inline_start_color, + BorderInlineStartColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_HC, + nullptr, + BorderColor, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-inline-start-style, + border_inline_start_style, + BorderInlineStartStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_HK, + kBorderStyleKTable, + BorderStyle, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + border-inline-start-width, + border_inline_start_width, + BorderInlineStartWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + BorderWidth, + Border, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_SHORTHAND( + border-left, + border_left, + BorderLeft, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BORDER( + border-left-color, + border_left_color, + BorderLeftColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleBorder, mBorderLeftColor), + eStyleAnimType_ComplexColor) +CSS_PROP_BORDER( + -moz-border-left-colors, + border_left_colors, + CSS_PROP_DOMPROP_PREFIXED(BorderLeftColors), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-left-style, + border_left_style, + BorderLeftStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HK, + kBorderStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-left-width, + border_left_width, + BorderLeftWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SHORTHAND( + border-radius, + border_radius, + BorderRadius, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SHORTHAND( + border-right, + border_right, + BorderRight, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BORDER( + border-right-color, + border_right_color, + BorderRightColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleBorder, mBorderRightColor), + eStyleAnimType_ComplexColor) +CSS_PROP_BORDER( + -moz-border-right-colors, + border_right_colors, + CSS_PROP_DOMPROP_PREFIXED(BorderRightColors), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-right-style, + border_right_style, + BorderRightStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HK, + kBorderStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-right-width, + border_right_width, + BorderRightWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_TABLEBORDER( + border-spacing, + border_spacing, + BorderSpacing, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SHORTHAND( + border-style, + border_style, + BorderStyle, + CSS_PROPERTY_PARSE_FUNCTION, + "") // on/off will need reflow +CSS_PROP_SHORTHAND( + border-top, + border_top, + BorderTop, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_BORDER( + border-top-color, + border_top_color, + BorderTopColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleBorder, mBorderTopColor), + eStyleAnimType_ComplexColor) +CSS_PROP_BORDER( + -moz-border-top-colors, + border_top_colors, + CSS_PROP_DOMPROP_PREFIXED(BorderTopColors), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + border-top-left-radius, + border_top_left_radius, + BorderTopLeftRadius, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleBorder, mBorderRadius), + eStyleAnimType_Corner_TopLeft) +CSS_PROP_BORDER( + border-top-right-radius, + border_top_right_radius, + BorderTopRightRadius, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleBorder, mBorderRadius), + eStyleAnimType_Corner_TopRight) +CSS_PROP_BORDER( + border-top-style, + border_top_style, + BorderTopStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HK, + kBorderStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // on/off will need reflow +CSS_PROP_BORDER( + border-top-width, + border_top_width, + BorderTopWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SHORTHAND( + border-width, + border_width, + BorderWidth, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "") +CSS_PROP_POSITION( + bottom, + bottom, + Bottom, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mOffset), + eStyleAnimType_Sides_Bottom) +CSS_PROP_XUL( + -moz-box-align, + box_align, + CSS_PROP_DOMPROP_PREFIXED(BoxAlign), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBoxAlignKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_BORDER( + box-decoration-break, + box_decoration_break, + BoxDecorationBreak, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.box-decoration-break.enabled", + VARIANT_HK, + kBoxDecorationBreakKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_XUL( + -moz-box-direction, + box_direction, + CSS_PROP_DOMPROP_PREFIXED(BoxDirection), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBoxDirectionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_XUL( + -moz-box-flex, + box_flex, + CSS_PROP_DOMPROP_PREFIXED(BoxFlex), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HN, + nullptr, + offsetof(nsStyleXUL, mBoxFlex), + eStyleAnimType_float) // XXX bug 3935 +CSS_PROP_XUL( + -moz-box-ordinal-group, + box_ordinal_group, + CSS_PROP_DOMPROP_PREFIXED(BoxOrdinalGroup), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HI, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_XUL( + -moz-box-orient, + box_orient, + CSS_PROP_DOMPROP_PREFIXED(BoxOrient), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBoxOrientKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_XUL( + -moz-box-pack, + box_pack, + CSS_PROP_DOMPROP_PREFIXED(BoxPack), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBoxPackKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_EFFECTS( + box-shadow, + box_shadow, + BoxShadow, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + // NOTE: some components must be nonnegative + "", + 0, + kBoxShadowTypeKTable, + offsetof(nsStyleEffects, mBoxShadow), + eStyleAnimType_Shadow) +CSS_PROP_POSITION( + box-sizing, + box_sizing, + BoxSizing, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBoxSizingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TABLEBORDER( + caption-side, + caption_side, + CaptionSide, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kCaptionSideKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + clear, + clear, + Clear, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kClearKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_EFFECTS( + clip, + clip, + Clip, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + 0, + nullptr, + offsetof(nsStyleEffects, mClip), + eStyleAnimType_Custom) +CSS_PROP_SVGRESET( + clip-path, + clip_path, + ClipPath, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SVG( + clip-rule, + clip_rule, + ClipRule, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kFillRuleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_COLOR( + color, + color, + Color, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED | + CSS_PROPERTY_HASHLESS_COLOR_QUIRK, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleColor, mColor), + eStyleAnimType_Color) +CSS_PROP_VISIBILITY( + color-adjust, + color_adjust, + ColorAdjust, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.color-adjust.enabled", + VARIANT_HK, + kColorAdjustKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + color-interpolation, + color_interpolation, + ColorInterpolation, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kColorInterpolationKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + color-interpolation-filters, + color_interpolation_filters, + ColorInterpolationFilters, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kColorInterpolationKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_COLUMN( + column-count, + column_count, + ColumnCount, + CSS_PROPERTY_PARSE_VALUE | + // Need to reject 0 in addition to negatives. If we accept 0, we + // need to change NS_STYLE_COLUMN_COUNT_AUTO to something else. + CSS_PROPERTY_VALUE_AT_LEAST_ONE, + "", + VARIANT_AHI, + nullptr, + offsetof(nsStyleColumn, mColumnCount), + eStyleAnimType_Custom) +CSS_PROP_COLUMN( + column-fill, + column_fill, + ColumnFill, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kColumnFillKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_COLUMN( + column-gap, + column_gap, + ColumnGap, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HL | VARIANT_NORMAL | VARIANT_CALC, + nullptr, + offsetof(nsStyleColumn, mColumnGap), + eStyleAnimType_Coord) +CSS_PROP_SHORTHAND( + column-rule, + column_rule, + ColumnRule, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_COLUMN( + column-rule-color, + column_rule_color, + ColumnRuleColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleColumn, mColumnRuleColor), + eStyleAnimType_ComplexColor) +CSS_PROP_COLUMN( + column-rule-style, + column_rule_style, + ColumnRuleStyle, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kBorderStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_COLUMN( + column-rule-width, + column_rule_width, + ColumnRuleWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_COLUMN( + column-width, + column_width, + ColumnWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_AHL | VARIANT_CALC, + nullptr, + offsetof(nsStyleColumn, mColumnWidth), + eStyleAnimType_Coord) +CSS_PROP_SHORTHAND( + columns, + columns, + Columns, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_DISPLAY( + contain, + contain, + Contain, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_FIXPOS_CB, + "layout.css.contain.enabled", + // Does not affect parsing, but is needed for tab completion in devtools: + VARIANT_HK | VARIANT_NONE, + kContainKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_CONTENT( + content, + content, + Content, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + 0, + kContentKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_TEXT( + -moz-control-character-visibility, + _moz_control_character_visibility, + CSS_PROP_DOMPROP_PREFIXED(ControlCharacterVisibility), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kControlCharacterVisibilityKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_CONTENT( + counter-increment, + counter_increment, + CounterIncrement, + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 137285 +CSS_PROP_CONTENT( + counter-reset, + counter_reset, + CounterReset, + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 137285 +CSS_PROP_USERINTERFACE( + cursor, + cursor, + Cursor, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_START_IMAGE_LOADS | + CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0, + "", + 0, + kCursorKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_VISIBILITY( + direction, + direction, + Direction, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kDirectionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND) +CSS_PROP_DISPLAY( + display, + display, + Display, + CSS_PROPERTY_PARSE_VALUE | + // This is allowed because we need to make the placeholder + // pseudo-element an inline-block in the UA stylesheet. It is a block + // by default. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kDisplayKTable, + offsetof(nsStyleDisplay, mDisplay), + eStyleAnimType_None) +CSS_PROP_SVGRESET( + dominant-baseline, + dominant_baseline, + DominantBaseline, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kDominantBaselineKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TABLEBORDER( + empty-cells, + empty_cells, + EmptyCells, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kEmptyCellsKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + fill, + fill, + Fill, + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + kContextPatternKTable, + offsetof(nsStyleSVG, mFill), + eStyleAnimType_PaintServer) +CSS_PROP_SVG( + fill-opacity, + fill_opacity, + FillOpacity, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD, + kContextOpacityKTable, + offsetof(nsStyleSVG, mFillOpacity), + eStyleAnimType_float) +CSS_PROP_SVG( + fill-rule, + fill_rule, + FillRule, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kFillRuleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_EFFECTS( + filter, + filter, + Filter, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_FIXPOS_CB, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SHORTHAND( + flex, + flex, + Flex, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_POSITION( + flex-basis, + flex_basis, + FlexBasis, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mFlexBasis), + eStyleAnimType_Coord) +CSS_PROP_POSITION( + flex-direction, + flex_direction, + FlexDirection, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kFlexDirectionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + flex-flow, + flex_flow, + FlexFlow, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_POSITION( + flex-grow, + flex_grow, + FlexGrow, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. + VARIANT_HN, + nullptr, + offsetof(nsStylePosition, mFlexGrow), + eStyleAnimType_float) +CSS_PROP_POSITION( + flex-shrink, + flex_shrink, + FlexShrink, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. + VARIANT_HN, + nullptr, + offsetof(nsStylePosition, mFlexShrink), + eStyleAnimType_float) +CSS_PROP_POSITION( + flex-wrap, + flex_wrap, + FlexWrap, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kFlexWrapKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + float, + float_, + CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "", + VARIANT_HK, + kFloatKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_BORDER( + -moz-float-edge, + float_edge, + CSS_PROP_DOMPROP_PREFIXED(FloatEdge), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kFloatEdgeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_SVGRESET( + flood-color, + flood_color, + FloodColor, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleSVGReset, mFloodColor), + eStyleAnimType_Color) +CSS_PROP_SVGRESET( + flood-opacity, + flood_opacity, + FloodOpacity, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HN, + nullptr, + offsetof(nsStyleSVGReset, mFloodOpacity), + eStyleAnimType_float) +CSS_PROP_SHORTHAND( + font, + font, + Font, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_FONT( + font-family, + font_family, + FontFamily, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-feature-settings, + font_feature_settings, + FontFeatureSettings, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-kerning, + font_kerning, + FontKerning, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kFontKerningKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-language-override, + font_language_override, + FontLanguageOverride, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_NORMAL | VARIANT_INHERIT | VARIANT_STRING, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-size, + font_size, + FontSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HKLP | VARIANT_SYSFONT | VARIANT_CALC, + kFontSizeKTable, + // Note that mSize is the correct place for *reading* the computed value, + // but setting it requires setting mFont.size as well. + offsetof(nsStyleFont, mSize), + eStyleAnimType_nscoord) +CSS_PROP_FONT( + font-size-adjust, + font_size_adjust, + FontSizeAdjust, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HON | VARIANT_SYSFONT, + nullptr, + offsetof(nsStyleFont, mFont.sizeAdjust), + eStyleAnimType_float) +CSS_PROP_FONT( + font-stretch, + font_stretch, + FontStretch, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK | VARIANT_SYSFONT, + kFontStretchKTable, + offsetof(nsStyleFont, mFont.stretch), + eStyleAnimType_Custom) +CSS_PROP_FONT( + font-style, + font_style, + FontStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK | VARIANT_SYSFONT, + kFontStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-synthesis, + font_synthesis, + FontSynthesis, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontSynthesisKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + font-variant, + font_variant, + FontVariant, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_FONT( + font-variant-alternates, + font_variant_alternates, + FontVariantAlternates, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontVariantAlternatesKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-variant-caps, + font_variant_caps, + FontVariantCaps, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HMK, + kFontVariantCapsKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-variant-east-asian, + font_variant_east_asian, + FontVariantEastAsian, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontVariantEastAsianKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-variant-ligatures, + font_variant_ligatures, + FontVariantLigatures, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontVariantLigaturesKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-variant-numeric, + font_variant_numeric, + FontVariantNumeric, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontVariantNumericKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-variant-position, + font_variant_position, + FontVariantPosition, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HMK, + kFontVariantPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + font-weight, + font_weight, + FontWeight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + // NOTE: This property has range restrictions on interpolation! + "", + 0, + kFontWeightKTable, + offsetof(nsStyleFont, mFont.weight), + eStyleAnimType_Custom) +CSS_PROP_UIRESET( + -moz-force-broken-image-icon, + force_broken_image_icon, + CSS_PROP_DOMPROP_PREFIXED(ForceBrokenImageIcon), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HI, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // bug 58646 +CSS_PROP_SHORTHAND( + grid, + grid, + Grid, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_SHORTHAND( + grid-area, + grid_area, + GridArea, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_POSITION( + grid-auto-columns, + grid_auto_columns, + GridAutoColumns, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + kGridTrackBreadthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-auto-flow, + grid_auto_flow, + GridAutoFlow, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + kGridAutoFlowKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-auto-rows, + grid_auto_rows, + GridAutoRows, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + kGridTrackBreadthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + grid-column, + grid_column, + GridColumn, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_POSITION( + grid-column-end, + grid_column_end, + GridColumnEnd, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-column-gap, + grid_column_gap, + GridColumnGap, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mGridColumnGap), + eStyleAnimType_Coord) +CSS_PROP_POSITION( + grid-column-start, + grid_column_start, + GridColumnStart, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + grid-gap, + grid_gap, + GridGap, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_SHORTHAND( + grid-row, + grid_row, + GridRow, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_POSITION( + grid-row-end, + grid_row_end, + GridRowEnd, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-row-gap, + grid_row_gap, + GridRowGap, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mGridRowGap), + eStyleAnimType_Coord) +CSS_PROP_POSITION( + grid-row-start, + grid_row_start, + GridRowStart, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + grid-template, + grid_template, + GridTemplate, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.grid.enabled") +CSS_PROP_POSITION( + grid-template-areas, + grid_template_areas, + GridTemplateAreas, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-template-columns, + grid_template_columns, + GridTemplateColumns, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + kGridTrackBreadthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + grid-template-rows, + grid_template_rows, + GridTemplateRows, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.grid.enabled", + 0, + kGridTrackBreadthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + height, + height, + Height, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mHeight), + eStyleAnimType_Coord) +CSS_PROP_TEXT( + hyphens, + hyphens, + Hyphens, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kHyphensKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXTRESET( + initial-letter, + initial_letter, + InitialLetter, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "layout.css.initial-letter.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_VISIBILITY( + image-orientation, + image_orientation, + ImageOrientation, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "layout.css.image-orientation.enabled", + 0, + kImageOrientationKTable, + offsetof(nsStyleVisibility, mImageOrientation), + eStyleAnimType_Discrete) +CSS_PROP_LIST( + -moz-image-region, + image_region, + CSS_PROP_DOMPROP_PREFIXED(ImageRegion), + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + nullptr, + offsetof(nsStyleList, mImageRegion), + eStyleAnimType_Custom) +CSS_PROP_VISIBILITY( + image-rendering, + image_rendering, + ImageRendering, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kImageRenderingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_UIRESET( + ime-mode, + ime_mode, + ImeMode, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kIMEModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_LOGICAL( + inline-size, + inline_size, + InlineSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + Size, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + isolation, + isolation, + Isolation, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT, + "layout.css.isolation.enabled", + VARIANT_HK, + kIsolationKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + justify-content, + justify_content, + JustifyContent, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + kAutoCompletionAlignJustifyContent, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + justify-items, + justify_items, + JustifyItems, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + // for auto-completion we use same values as justify-self: + kAutoCompletionAlignJustifySelf, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + justify-self, + justify_self, + JustifySelf, + CSS_PROPERTY_PARSE_FUNCTION, + "", + VARIANT_HK, + kAutoCompletionAlignJustifySelf, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -x-lang, + _x_lang, + Lang, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_POSITION( + left, + left, + Left, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mOffset), + eStyleAnimType_Sides_Left) +CSS_PROP_TEXT( + letter-spacing, + letter_spacing, + LetterSpacing, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HL | VARIANT_NORMAL | VARIANT_CALC, + nullptr, + offsetof(nsStyleText, mLetterSpacing), + eStyleAnimType_Coord) +CSS_PROP_SVGRESET( + lighting-color, + lighting_color, + LightingColor, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleSVGReset, mLightingColor), + eStyleAnimType_Color) +CSS_PROP_TEXT( + line-height, + line_height, + LineHeight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HLPN | VARIANT_KEYWORD | VARIANT_NORMAL | VARIANT_SYSFONT | VARIANT_CALC, + kLineHeightKTable, + offsetof(nsStyleText, mLineHeight), + eStyleAnimType_Coord) +CSS_PROP_SHORTHAND( + list-style, + list_style, + ListStyle, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_LIST( + list-style-image, + list_style_image, + ListStyleImage, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_LIST( + list-style-position, + list_style_position, + ListStylePosition, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kListStylePositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_LIST( + list-style-type, + list_style_type, + ListStyleType, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + margin, + margin, + Margin, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE, + "") +CSS_PROP_LOGICAL( + margin-block-end, + margin_block_end, + MarginBlockEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Margin, + Margin, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + margin-block-start, + margin_block_start, + MarginBlockStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Margin, + Margin, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_MARGIN( + margin-bottom, + margin_bottom, + MarginBottom, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStyleMargin, mMargin), + eStyleAnimType_Sides_Bottom) +CSS_PROP_LOGICAL( + margin-inline-end, + margin_inline_end, + MarginInlineEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Margin, + Margin, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + margin-inline-start, + margin_inline_start, + MarginInlineStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Margin, + Margin, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_MARGIN( + margin-left, + margin_left, + MarginLeft, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStyleMargin, mMargin), + eStyleAnimType_Sides_Left) +CSS_PROP_MARGIN( + margin-right, + margin_right, + MarginRight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStyleMargin, mMargin), + eStyleAnimType_Sides_Right) +CSS_PROP_MARGIN( + margin-top, + margin_top, + MarginTop, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_PAGE_RULE | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStyleMargin, mMargin), + eStyleAnimType_Sides_Top) +CSS_PROP_SHORTHAND( + marker, + marker, + Marker, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SVG( + marker-end, + marker_end, + MarkerEnd, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + marker-mid, + marker_mid, + MarkerMid, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + marker-start, + marker_start, + MarkerStart, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef MOZ_ENABLE_MASK_AS_SHORTHAND +CSS_PROP_SVGRESET( + mask, + mask, + Mask, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT, + "", + VARIANT_HUO, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#else +CSS_PROP_SHORTHAND( + mask, + mask, + Mask, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SVGRESET( + mask-clip, + mask_clip, + MaskClip, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerOriginKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + mask-composite, + mask_composite, + MaskComposite, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerCompositeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + mask-image, + mask_image, + MaskImage, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_START_IMAGE_LOADS, + "", + VARIANT_IMAGE, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + mask-mode, + mask_mode, + MaskMode, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + mask-origin, + mask_origin, + MaskOrigin, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerOriginKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + mask-position, + mask_position, + MaskPosition, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "") +CSS_PROP_SVGRESET( + mask-position-x, + mask_position_x, + MaskPositionX, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SVGRESET( + mask-position-y, + mask_position_y, + MaskPositionY, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SVGRESET( + mask-repeat, + mask_repeat, + MaskRepeat, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD, // used by list parsing + kImageLayerRepeatKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + mask-size, + mask_size, + MaskSize, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + kImageLayerSizeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +#endif // MOZ_ENABLE_MASK_AS_SHORTHAND +CSS_PROP_SVGRESET( + mask-type, + mask_type, + MaskType, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kMaskTypeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -moz-math-display, + math_display, + MathDisplay, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kMathDisplayKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + -moz-math-variant, + math_variant, + MathVariant, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + VARIANT_HK, + kMathVariantKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_LOGICAL( + max-block-size, + max_block_size, + MaxBlockSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_HLPO | VARIANT_CALC, + nullptr, + MaxSize, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_POSITION( + max-height, + max_height, + MaxHeight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HKLPO | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mMaxHeight), + eStyleAnimType_Coord) +CSS_PROP_LOGICAL( + max-inline-size, + max_inline_size, + MaxInlineSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS, + "", + VARIANT_HKLPO | VARIANT_CALC, + kWidthKTable, + MaxSize, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_POSITION( + max-width, + max_width, + MaxWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HKLPO | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mMaxWidth), + eStyleAnimType_Coord) +CSS_PROP_LOGICAL( + min-block-size, + min_block_size, + MinBlockSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + MinSize, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -moz-min-font-size-ratio, + _moz_min_font_size_ratio, + CSS_PROP_DOMPROP_PREFIXED(MinFontSizeRatio), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "", + VARIANT_INHERIT | VARIANT_PERCENT, + nullptr, + offsetof(nsStyleFont, mMinFontSizeRatio), + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_POSITION( + min-height, + min_height, + MinHeight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mMinHeight), + eStyleAnimType_Coord) +CSS_PROP_LOGICAL( + min-inline-size, + min_inline_size, + MinInlineSize, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_AXIS, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + MinSize, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_POSITION( + min-width, + min_width, + MinWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mMinWidth), + eStyleAnimType_Coord) +CSS_PROP_EFFECTS( + mix-blend-mode, + mix_blend_mode, + MixBlendMode, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT, + "layout.css.mix-blend-mode.enabled", + VARIANT_HK, + kBlendModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + object-fit, + object_fit, + ObjectFit, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.object-fit-and-position.enabled", + VARIANT_HK, + kObjectFitKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + object-position, + object_position, + ObjectPosition, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "layout.css.object-fit-and-position.enabled", + 0, + kImageLayerPositionKTable, + offsetof(nsStylePosition, mObjectPosition), + eStyleAnimType_Custom) +CSS_PROP_LOGICAL( + offset-block-end, + offset_block_end, + OffsetBlockEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Offset, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + offset-block-start, + offset_block_start, + OffsetBlockStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Offset, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + offset-inline-end, + offset_inline_end, + OffsetInlineEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Offset, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + offset-inline-start, + offset_inline_start, + OffsetInlineStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + Offset, + Position, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_EFFECTS( + opacity, + opacity, + Opacity, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR | + CSS_PROPERTY_CREATES_STACKING_CONTEXT, + "", + VARIANT_HN, + nullptr, + offsetof(nsStyleEffects, mOpacity), + eStyleAnimType_float) +CSS_PROP_POSITION( + order, + order, + Order, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HI, + nullptr, + offsetof(nsStylePosition, mOrder), + eStyleAnimType_Custom) // +CSS_PROP_DISPLAY( + -moz-orient, + orient, + CSS_PROP_DOMPROP_PREFIXED(Orient), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kOrientKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_FONT( + -moz-osx-font-smoothing, + osx_font_smoothing, + CSS_PROP_DOMPROP_PREFIXED(OsxFontSmoothing), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.osx-font-smoothing.enabled", + VARIANT_HK, + kFontSmoothingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + outline, + outline, + Outline, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_OUTLINE( + outline-color, + outline_color, + OutlineColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleOutline, mOutlineColor), + eStyleAnimType_ComplexColor) +CSS_PROP_OUTLINE( + outline-offset, + outline_offset, + OutlineOffset, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HL | VARIANT_CALC, + nullptr, + offsetof(nsStyleOutline, mOutlineOffset), + eStyleAnimType_nscoord) +CSS_PROP_SHORTHAND( + -moz-outline-radius, + _moz_outline_radius, + CSS_PROP_DOMPROP_PREFIXED(OutlineRadius), + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_OUTLINE( + -moz-outline-radius-bottomleft, + _moz_outline_radius_bottomLeft, + CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomleft), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleOutline, mOutlineRadius), + eStyleAnimType_Corner_BottomLeft) +CSS_PROP_OUTLINE( + -moz-outline-radius-bottomright, + _moz_outline_radius_bottomRight, + CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomright), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleOutline, mOutlineRadius), + eStyleAnimType_Corner_BottomRight) +CSS_PROP_OUTLINE( + -moz-outline-radius-topleft, + _moz_outline_radius_topLeft, + CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopleft), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleOutline, mOutlineRadius), + eStyleAnimType_Corner_TopLeft) +CSS_PROP_OUTLINE( + -moz-outline-radius-topright, + _moz_outline_radius_topRight, + CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopright), + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC, + "", + 0, + nullptr, + offsetof(nsStyleOutline, mOutlineRadius), + eStyleAnimType_Corner_TopRight) +CSS_PROP_OUTLINE( + outline-style, + outline_style, + OutlineStyle, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kOutlineStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_OUTLINE( + outline-width, + outline_width, + OutlineWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + offsetof(nsStyleOutline, mOutlineWidth), + eStyleAnimType_Coord) +CSS_PROP_SHORTHAND( + overflow, + overflow, + Overflow, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_DISPLAY( + overflow-clip-box, + overflow_clip_box, + OverflowClipBox, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.overflow-clip-box.enabled", + VARIANT_HK, + kOverflowClipBoxKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + overflow-x, + overflow_x, + OverflowX, + CSS_PROPERTY_PARSE_VALUE | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kOverflowSubKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + overflow-y, + overflow_y, + OverflowY, + CSS_PROPERTY_PARSE_VALUE | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kOverflowSubKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + padding, + padding, + Padding, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "") +CSS_PROP_LOGICAL( + padding-block-end, + padding_block_end, + PaddingBlockEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + Padding, + Padding, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + padding-block-start, + padding_block_start, + PaddingBlockStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_BLOCK_AXIS, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + Padding, + Padding, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_PADDING( + padding-bottom, + padding_bottom, + PaddingBottom, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePadding, mPadding), + eStyleAnimType_Sides_Bottom) +CSS_PROP_LOGICAL( + padding-inline-end, + padding_inline_end, + PaddingInlineEnd, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL | + CSS_PROPERTY_LOGICAL_END_EDGE, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + Padding, + Padding, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_LOGICAL( + padding-inline-start, + padding_inline_start, + PaddingInlineStart, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_LOGICAL, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + Padding, + Padding, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_PADDING( + padding-left, + padding_left, + PaddingLeft, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePadding, mPadding), + eStyleAnimType_Sides_Left) +CSS_PROP_PADDING( + padding-right, + padding_right, + PaddingRight, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePadding, mPadding), + eStyleAnimType_Sides_Right) +CSS_PROP_PADDING( + padding-top, + padding_top, + PaddingTop, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePadding, mPadding), + eStyleAnimType_Sides_Top) +CSS_PROP_DISPLAY( + page-break-after, + page_break_after, + PageBreakAfter, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kPageBreakKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // temp fix for bug 24000 +CSS_PROP_DISPLAY( + page-break-before, + page_break_before, + PageBreakBefore, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kPageBreakKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // temp fix for bug 24000 +CSS_PROP_DISPLAY( + page-break-inside, + page_break_inside, + PageBreakInside, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kPageBreakInsideKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + paint-order, + paint_order, + PaintOrder, + CSS_PROPERTY_PARSE_FUNCTION, + "svg.paint-order.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + perspective, + perspective, + Perspective, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_FIXPOS_CB, + "", + VARIANT_NONE | VARIANT_INHERIT | VARIANT_LENGTH | + VARIANT_NONNEGATIVE_DIMENSION, + nullptr, + offsetof(nsStyleDisplay, mChildPerspective), + eStyleAnimType_Coord) +CSS_PROP_DISPLAY( + perspective-origin, + perspective_origin, + PerspectiveOrigin, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_SHORTHAND( + place-content, + place_content, + PlaceContent, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SHORTHAND( + place-items, + place_items, + PlaceItems, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_SHORTHAND( + place-self, + place_self, + PlaceSelf, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_USERINTERFACE( + pointer-events, + pointer_events, + PointerEvents, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kPointerEventsKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + position, + position, + Position, + CSS_PROPERTY_PARSE_VALUE | + // For position: sticky/fixed + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_ABSPOS_CB, + "", + VARIANT_HK, + kPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_LIST( + quotes, + quotes, + Quotes, + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + resize, + resize, + Resize, + CSS_PROPERTY_PARSE_VALUE | + // This is allowed because the UA stylesheet sets 'resize: both;' on + // textarea and we need to disable this for the placeholder + // pseudo-element. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kResizeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + right, + right, + Right, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mOffset), + eStyleAnimType_Sides_Right) +CSS_PROP_TEXT( + ruby-align, + ruby_align, + RubyAlign, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kRubyAlignKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + ruby-position, + ruby_position, + RubyPosition, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kRubyPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -moz-script-level, + script_level, + ScriptLevel, + // We only allow 'script-level' when unsafe rules are enabled, because + // otherwise it could interfere with rulenode optimizations if used in + // a non-MathML-enabled document. + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_PARSE_VALUE, + "", + // script-level can take Auto, Integer and Number values, but only Auto + // ("increment if parent is not in displaystyle") and Integer + // ("relative") values can be specified in a style sheet. + VARIANT_AHI, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + -moz-script-min-size, + script_min_size, + ScriptMinSize, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + -moz-script-size-multiplier, + script_size_multiplier, + ScriptSizeMultiplier, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_DISPLAY( + scroll-behavior, + scroll_behavior, + ScrollBehavior, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.scroll-behavior.property-enabled", + VARIANT_HK, + kScrollBehaviorKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + scroll-snap-coordinate, + scroll_snap_coordinate, + ScrollSnapCoordinate, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_STORES_CALC, + "layout.css.scroll-snap.enabled", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + scroll-snap-destination, + scroll_snap_destination, + ScrollSnapDestination, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_STORES_CALC, + "layout.css.scroll-snap.enabled", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + scroll-snap-points-x, + scroll_snap_points_x, + ScrollSnapPointsX, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_STORES_CALC, + "layout.css.scroll-snap.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + scroll-snap-points-y, + scroll_snap_points_y, + ScrollSnapPointsY, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_STORES_CALC, + "layout.css.scroll-snap.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + scroll-snap-type, + scroll_snap_type, + ScrollSnapType, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.scroll-snap.enabled") +CSS_PROP_DISPLAY( + scroll-snap-type-x, + scroll_snap_type_x, + ScrollSnapTypeX, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.scroll-snap.enabled", + VARIANT_HK, + kScrollSnapTypeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + scroll-snap-type-y, + scroll_snap_type_y, + ScrollSnapTypeY, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.scroll-snap.enabled", + VARIANT_HK, + kScrollSnapTypeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + shape-outside, + shape_outside, + ShapeOutside, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, + "layout.css.shape-outside.enabled", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // FIXME: Bug 1289049 for adding animation support +CSS_PROP_SVG( + shape-rendering, + shape_rendering, + ShapeRendering, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kShapeRenderingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_TABLE( + -x-span, + _x_span, + Span, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_XUL( + -moz-stack-sizing, + stack_sizing, + CSS_PROP_DOMPROP_PREFIXED(StackSizing), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kStackSizingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVGRESET( + stop-color, + stop_color, + StopColor, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleSVGReset, mStopColor), + eStyleAnimType_Color) +CSS_PROP_SVGRESET( + stop-opacity, + stop_opacity, + StopOpacity, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HN, + nullptr, + offsetof(nsStyleSVGReset, mStopOpacity), + eStyleAnimType_float) +CSS_PROP_SVG( + stroke, + stroke, + Stroke, + CSS_PROPERTY_PARSE_FUNCTION, + "", + 0, + kContextPatternKTable, + offsetof(nsStyleSVG, mStroke), + eStyleAnimType_PaintServer) +CSS_PROP_SVG( + stroke-dasharray, + stroke_dasharray, + StrokeDasharray, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_NUMBERS_ARE_PIXELS, + // NOTE: Internal values have range restrictions. + "", + 0, + kStrokeContextValueKTable, + CSS_PROP_NO_OFFSET, /* property stored in 2 separate members */ + eStyleAnimType_Custom) +CSS_PROP_SVG( + stroke-dashoffset, + stroke_dashoffset, + StrokeDashoffset, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_NUMBERS_ARE_PIXELS, + "", + VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD, + kStrokeContextValueKTable, + offsetof(nsStyleSVG, mStrokeDashoffset), + eStyleAnimType_Coord) +CSS_PROP_SVG( + stroke-linecap, + stroke_linecap, + StrokeLinecap, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kStrokeLinecapKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + stroke-linejoin, + stroke_linejoin, + StrokeLinejoin, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kStrokeLinejoinKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SVG( + stroke-miterlimit, + stroke_miterlimit, + StrokeMiterlimit, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_AT_LEAST_ONE, + "", + VARIANT_HN, + nullptr, + offsetof(nsStyleSVG, mStrokeMiterlimit), + eStyleAnimType_float) +CSS_PROP_SVG( + stroke-opacity, + stroke_opacity, + StrokeOpacity, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD, + kContextOpacityKTable, + offsetof(nsStyleSVG, mStrokeOpacity), + eStyleAnimType_float) +CSS_PROP_SVG( + stroke-width, + stroke_width, + StrokeWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_NUMBERS_ARE_PIXELS, + "", + VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD, + kStrokeContextValueKTable, + offsetof(nsStyleSVG, mStrokeWidth), + eStyleAnimType_Coord) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -x-system-font, + _x_system_font, + CSS_PROP_DOMPROP_PREFIXED(SystemFont), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kFontKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_TEXT( + -moz-tab-size, + _moz_tab_size, + CSS_PROP_DOMPROP_PREFIXED(TabSize), + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE, + "", + VARIANT_HI, + nullptr, + offsetof(nsStyleText, mTabSize), + eStyleAnimType_Discrete) +CSS_PROP_TABLE( + table-layout, + table_layout, + TableLayout, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kTableLayoutKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-align, + text_align, + TextAlign, + CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + // When we support aligning on a string, we can parse text-align + // as a string.... + VARIANT_HK /* | VARIANT_STRING */, + kTextAlignKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-align-last, + text_align_last, + TextAlignLast, + CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "", + VARIANT_HK, + kTextAlignLastKTable, + offsetof(nsStyleText, mTextAlignLast), + eStyleAnimType_Discrete) +CSS_PROP_SVG( + text-anchor, + text_anchor, + TextAnchor, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kTextAnchorKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-combine-upright, + text_combine_upright, + TextCombineUpright, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "layout.css.text-combine-upright.enabled", + 0, + kTextCombineUprightKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + text-decoration, + text_decoration, + TextDecoration, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_TEXTRESET( + text-decoration-color, + text_decoration_color, + TextDecorationColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleTextReset, mTextDecorationColor), + eStyleAnimType_ComplexColor) +CSS_PROP_TEXTRESET( + text-decoration-line, + text_decoration_line, + TextDecorationLine, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kTextDecorationLineKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXTRESET( + text-decoration-style, + text_decoration_style, + TextDecorationStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kTextDecorationStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + text-emphasis, + text_emphasis, + TextEmphasis, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_TEXT( + text-emphasis-color, + text_emphasis_color, + TextEmphasisColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "", + VARIANT_HC, + nullptr, + offsetof(nsStyleText, mTextEmphasisColor), + eStyleAnimType_ComplexColor) +CSS_PROP_TEXT( + text-emphasis-position, + text_emphasis_position, + TextEmphasisPosition, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "", + 0, + kTextEmphasisPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-emphasis-style, + text_emphasis_style, + TextEmphasisStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + -webkit-text-fill-color, + _webkit_text_fill_color, + WebkitTextFillColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "layout.css.prefixes.webkit", + VARIANT_HC, + nullptr, + offsetof(nsStyleText, mWebkitTextFillColor), + eStyleAnimType_ComplexColor) +CSS_PROP_TEXT( + text-indent, + text_indent, + TextIndent, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HLP | VARIANT_CALC, + nullptr, + offsetof(nsStyleText, mTextIndent), + eStyleAnimType_Coord) +CSS_PROP_VISIBILITY( + text-orientation, + text_orientation, + TextOrientation, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kTextOrientationKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXTRESET( + text-overflow, + text_overflow, + TextOverflow, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + 0, + kTextOverflowKTable, + offsetof(nsStyleTextReset, mTextOverflow), + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-rendering, + text_rendering, + TextRendering, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kTextRenderingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-shadow, + text_shadow, + TextShadow, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + // NOTE: some components must be nonnegative + "", + 0, + nullptr, + offsetof(nsStyleText, mTextShadow), + eStyleAnimType_Shadow) +CSS_PROP_TEXT( + -moz-text-size-adjust, + text_size_adjust, + CSS_PROP_DOMPROP_PREFIXED(TextSizeAdjust), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_AUTO | VARIANT_NONE | VARIANT_INHERIT, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + -webkit-text-stroke, + _webkit_text_stroke, + WebkitTextStroke, + CSS_PROPERTY_PARSE_FUNCTION, + "layout.css.prefixes.webkit") +CSS_PROP_TEXT( + -webkit-text-stroke-color, + _webkit_text_stroke_color, + WebkitTextStrokeColor, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED, + "layout.css.prefixes.webkit", + VARIANT_HC, + nullptr, + offsetof(nsStyleText, mWebkitTextStrokeColor), + eStyleAnimType_ComplexColor) +CSS_PROP_TEXT( + -webkit-text-stroke-width, + _webkit_text_stroke_width, + WebkitTextStrokeWidth, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.prefixes.webkit", + VARIANT_HKL | VARIANT_CALC, + kBorderWidthKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + text-transform, + text_transform, + TextTransform, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kTextTransformKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -x-text-zoom, + _x_text_zoom, + TextZoom, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_POSITION( + top, + top, + Top, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHLP | VARIANT_CALC, + nullptr, + offsetof(nsStylePosition, mOffset), + eStyleAnimType_Sides_Top) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_DISPLAY( + -moz-top-layer, + _moz_top_layer, + CSS_PROP_DOMPROP_PREFIXED(TopLayer), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "", + VARIANT_HK, + kTopLayerKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_DISPLAY( + touch-action, + touch_action, + TouchAction, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "layout.css.touch_action.enabled", + VARIANT_HK, + kTouchActionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + transform, + transform, + Transform, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR | + CSS_PROPERTY_FIXPOS_CB, + "", + 0, + nullptr, + offsetof(nsStyleDisplay, mSpecifiedTransform), + eStyleAnimType_Custom) +// This shorthand is essentially an alias, but it requires different +// parsing rules, and it therefore implemented as a shorthand. +CSS_PROP_SHORTHAND( + -moz-transform, + _moz_transform, + MozTransform, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_IS_ALIAS, + "layout.css.prefixes.transforms") +CSS_PROP_DISPLAY( + transform-box, + transform_box, + TransformBox, + CSS_PROPERTY_PARSE_VALUE, + "svg.transform-box.enabled", + VARIANT_HK, + kTransformBoxKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_DISPLAY( + transform-origin, + transform_origin, + TransformOrigin, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + 0, + kImageLayerPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Custom) +CSS_PROP_DISPLAY( + transform-style, + transform_style, + TransformStyle, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT | + CSS_PROPERTY_FIXPOS_CB, + "", + VARIANT_HK, + kTransformStyleKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_SHORTHAND( + transition, + transition, + Transition, + CSS_PROPERTY_PARSE_FUNCTION, + "") +CSS_PROP_DISPLAY( + transition-delay, + transition_delay, + TransitionDelay, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_TIME, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + transition-duration, + transition_duration, + TransitionDuration, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_TIME | VARIANT_NONNEGATIVE_DIMENSION, // used by list parsing + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + transition-property, + transition_property, + TransitionProperty, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_IDENTIFIER | VARIANT_NONE | VARIANT_ALL, // used only in shorthand + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_DISPLAY( + transition-timing-function, + transition_timing_function, + TransitionTimingFunction, + CSS_PROPERTY_PARSE_VALUE_LIST | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing + kTransitionTimingFunctionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_TEXTRESET( + unicode-bidi, + unicode_bidi, + UnicodeBidi, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kUnicodeBidiKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +CSS_PROP_USERINTERFACE( + -moz-user-focus, + user_focus, + CSS_PROP_DOMPROP_PREFIXED(UserFocus), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kUserFocusKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_USERINTERFACE( + -moz-user-input, + user_input, + CSS_PROP_DOMPROP_PREFIXED(UserInput), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kUserInputKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX ??? // XXX bug 3935 +CSS_PROP_USERINTERFACE( + -moz-user-modify, + user_modify, + CSS_PROP_DOMPROP_PREFIXED(UserModify), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kUserModifyKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_UIRESET( + -moz-user-select, + user_select, + CSS_PROP_DOMPROP_PREFIXED(UserSelect), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kUserSelectKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // XXX bug 3935 +CSS_PROP_SVGRESET( + vector-effect, + vector_effect, + VectorEffect, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kVectorEffectKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +// NOTE: vertical-align is only supposed to apply to :first-letter when +// 'float' is 'none', but we don't worry about that since it has no +// effect otherwise +CSS_PROP_DISPLAY( + vertical-align, + vertical_align, + VerticalAlign, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + "", + VARIANT_HKLP | VARIANT_CALC, + kVerticalAlignKTable, + offsetof(nsStyleDisplay, mVerticalAlign), + eStyleAnimType_Coord) +CSS_PROP_VISIBILITY( + visibility, + visibility, + Visibility, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kVisibilityKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) // reflow for collapse +CSS_PROP_TEXT( + white-space, + white_space, + WhiteSpace, + CSS_PROPERTY_PARSE_VALUE | + // This is required by the UA stylesheet and can't be overridden. + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "", + VARIANT_HK, + kWhitespaceKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + width, + width, + Width, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_NONNEGATIVE | + CSS_PROPERTY_STORES_CALC | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, + "", + VARIANT_AHKLP | VARIANT_CALC, + kWidthKTable, + offsetof(nsStylePosition, mWidth), + eStyleAnimType_Coord) +CSS_PROP_DISPLAY( + will-change, + will_change, + WillChange, + CSS_PROPERTY_PARSE_FUNCTION | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + "", + 0, + nullptr, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_UIRESET( + -moz-window-dragging, + _moz_window_dragging, + CSS_PROP_DOMPROP_PREFIXED(WindowDragging), + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kWindowDraggingKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_UIRESET( + -moz-window-shadow, + _moz_window_shadow, + CSS_PROP_DOMPROP_PREFIXED(WindowShadow), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME, + "", + VARIANT_HK, + kWindowShadowKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_TEXT( + word-break, + word_break, + WordBreak, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kWordBreakKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_TEXT( + word-spacing, + word_spacing, + WordSpacing, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_STORES_CALC, + "", + VARIANT_HLP | VARIANT_NORMAL | VARIANT_CALC, + nullptr, + offsetof(nsStyleText, mWordSpacing), + eStyleAnimType_Coord) +CSS_PROP_TEXT( + overflow-wrap, + overflow_wrap, + OverflowWrap, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kOverflowWrapKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_VISIBILITY( + writing-mode, + writing_mode, + WritingMode, + CSS_PROPERTY_PARSE_VALUE, + "", + VARIANT_HK, + kWritingModeKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_Discrete) +CSS_PROP_POSITION( + z-index, + z_index, + ZIndex, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_CREATES_STACKING_CONTEXT, + "", + VARIANT_AHI, + nullptr, + offsetof(nsStylePosition, mZIndex), + eStyleAnimType_Coord) + +#ifdef USED_CSS_PROP + +#undef USED_CSS_PROP +#undef CSS_PROP_FONT +#undef CSS_PROP_COLOR +#undef CSS_PROP_BACKGROUND +#undef CSS_PROP_LIST +#undef CSS_PROP_POSITION +#undef CSS_PROP_TEXT +#undef CSS_PROP_TEXTRESET +#undef CSS_PROP_DISPLAY +#undef CSS_PROP_VISIBILITY +#undef CSS_PROP_CONTENT +#undef CSS_PROP_USERINTERFACE +#undef CSS_PROP_UIRESET +#undef CSS_PROP_TABLE +#undef CSS_PROP_TABLEBORDER +#undef CSS_PROP_MARGIN +#undef CSS_PROP_PADDING +#undef CSS_PROP_BORDER +#undef CSS_PROP_OUTLINE +#undef CSS_PROP_XUL +#undef CSS_PROP_COLUMN +#undef CSS_PROP_SVG +#undef CSS_PROP_SVGRESET +#undef CSS_PROP_VARIABLES +#undef CSS_PROP_EFFECTS + +#else /* !defined(USED_CSS_PROP) */ + +#ifdef DEFINED_CSS_PROP_FONT +#undef CSS_PROP_FONT +#undef DEFINED_CSS_PROP_FONT +#endif +#ifdef DEFINED_CSS_PROP_COLOR +#undef CSS_PROP_COLOR +#undef DEFINED_CSS_PROP_COLOR +#endif +#ifdef DEFINED_CSS_PROP_BACKGROUND +#undef CSS_PROP_BACKGROUND +#undef DEFINED_CSS_PROP_BACKGROUND +#endif +#ifdef DEFINED_CSS_PROP_LIST +#undef CSS_PROP_LIST +#undef DEFINED_CSS_PROP_LIST +#endif +#ifdef DEFINED_CSS_PROP_POSITION +#undef CSS_PROP_POSITION +#undef DEFINED_CSS_PROP_POSITION +#endif +#ifdef DEFINED_CSS_PROP_TEXT +#undef CSS_PROP_TEXT +#undef DEFINED_CSS_PROP_TETEXTRESETT +#endif +#ifdef DEFINED_CSS_PROP_TEXTRESET +#undef CSS_PROP_TEXTRESET +#undef DEFINED_CSS_PROP_TEDISPLAYTRESET +#endif +#ifdef DEFINED_CSS_PROP_DISPLAY +#undef CSS_PROP_DISPLAY +#undef DEFINED_CSS_PROP_DISPLAY +#endif +#ifdef DEFINED_CSS_PROP_VISIBILITY +#undef CSS_PROP_VISIBILITY +#undef DEFINED_CSS_PROP_VISIBILITY +#endif +#ifdef DEFINED_CSS_PROP_CONTENT +#undef CSS_PROP_CONTENT +#undef DEFINED_CSS_PROP_CONTENT +#endif +#ifdef DEFINED_CSS_PROP_USERINTERFACE +#undef CSS_PROP_USERINTERFACE +#undef DEFINED_CSS_PROP_USERINTERFACE +#endif +#ifdef DEFINED_CSS_PROP_UIRESET +#undef CSS_PROP_UIRESET +#undef DEFINED_CSS_PROP_UIRESET +#endif +#ifdef DEFINED_CSS_PROP_TABLE +#undef CSS_PROP_TABLE +#undef DEFINED_CSS_PROP_TABLE +#endif +#ifdef DEFINED_CSS_PROP_TABLEBORDER +#undef CSS_PROP_TABLEBORDER +#undef DEFINED_CSS_PROP_TABLEBORDER +#endif +#ifdef DEFINED_CSS_PROP_MARGIN +#undef CSS_PROP_MARGIN +#undef DEFINED_CSS_PROP_MARGIN +#endif +#ifdef DEFINED_CSS_PROP_PADDING +#undef CSS_PROP_PADDING +#undef DEFINED_CSS_PROP_PADDING +#endif +#ifdef DEFINED_CSS_PROP_BORDER +#undef CSS_PROP_BORDER +#undef DEFINED_CSS_PROP_BORDER +#endif +#ifdef DEFINED_CSS_PROP_OUTLINE +#undef CSS_PROP_OUTLINE +#undef DEFINED_CSS_PROP_OUTLINE +#endif +#ifdef DEFINED_CSS_PROP_XUL +#undef CSS_PROP_XUL +#undef DEFINED_CSS_PROP_XUL +#endif +#ifdef DEFINED_CSS_PROP_COLUMN +#undef CSS_PROP_COLUMN +#undef DEFINED_CSS_PROP_COLUMN +#endif +#ifdef DEFINED_CSS_PROP_SVG +#undef CSS_PROP_SVG +#undef DEFINED_CSS_PROP_SVG +#endif +#ifdef DEFINED_CSS_PROP_SVGRESET +#undef CSS_PROP_SVGRESET +#undef DEFINED_CSS_PROP_SVGRESET +#endif +#ifdef DEFINED_CSS_PROP_VARIABLES +#undef CSS_PROP_VARIABLES +#undef DEFINED_CSS_PROP_VARIABLES +#endif +#ifdef DEFINED_CSS_PROP_EFFECTS +#undef CSS_PROP_EFFECTS +#undef DEFINED_CSS_PROP_EFFECTS +#endif + +#endif /* !defined(USED_CSS_PROP) */ + +#ifdef DEFINED_CSS_PROP_SHORTHAND +#undef CSS_PROP_SHORTHAND +#undef DEFINED_CSS_PROP_SHORTHAND +#endif +#ifdef DEFINED_CSS_PROP_LOGICAL +#undef CSS_PROP_LOGICAL +#undef DEFINED_CSS_PROP_LOGICAL +#endif + +#undef CSS_PROP_DOMPROP_PREFIXED diff --git a/layout/style/nsCSSPropLogicalGroupList.h b/layout/style/nsCSSPropLogicalGroupList.h new file mode 100644 index 000000000..3d8a52bc9 --- /dev/null +++ b/layout/style/nsCSSPropLogicalGroupList.h @@ -0,0 +1,56 @@ +/* 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 groups of logical properties, for preprocessing + */ + +// A logical property group is one that defines the corresponding physical +// longhand properties that could be set by a given set of logical longhand +// properties. For example, the logical property group for margin-block-start +// (and the other three logical margin properties) is one that contains +// margin-top, margin-right, margin-bottom and margin-left. +// +// Logical property groups are defined below using one of the following +// macros, where the name_ argument must be capitalized LikeThis and +// must not collide with the name of a property's DOM method (its +// method_ in nsCSSPropList.h): +// +// CSS_PROP_LOGICAL_GROUP_SHORTHAND(name_) +// Defines a logical property group whose corresponding physical +// properties are those in a given shorthand. For example, the +// logical property group for margin-{block,inline}-{start,end} +// is defined by the margin shorthand. The name_ argument must +// be the method_ name of the shorthand (so Margin rather than +// margin). +// +// CSS_PROP_LOGICAL_GROUP_BOX(name_) +// Defines a logical property group whose corresponding physical +// properties are a set of four box properties which are not +// already represented by an existing shorthand property. For +// example, the logical property group for +// offset-{block,inline}-{start,end} contains the top, right, +// bottom and left physical properties, but there is no shorthand +// that sets those four properties. A table must be defined in +// nsCSSProps.cpp named gLogicalGroupTable containing the +// four physical properties in top/right/bottom/left order. +// +// CSS_PROP_LOGICAL_GROUP_AXIS(name_) +// Defines a logical property group whose corresponding physical +// properties are a set of two axis-related properties. For +// example, the logical property group for {block,inline}-size +// contains the width and height properties. A table must be +// defined in nCSSProps.cpp named gLogicalGroupTable +// containing the two physical properties in vertical/horizontal +// order, followed by an nsCSSProperty_UNKNOWN entry. + +CSS_PROP_LOGICAL_GROUP_SHORTHAND(BorderColor) +CSS_PROP_LOGICAL_GROUP_SHORTHAND(BorderStyle) +CSS_PROP_LOGICAL_GROUP_SHORTHAND(BorderWidth) +CSS_PROP_LOGICAL_GROUP_SHORTHAND(Margin) +CSS_PROP_LOGICAL_GROUP_AXIS(MaxSize) +CSS_PROP_LOGICAL_GROUP_BOX(Offset) +CSS_PROP_LOGICAL_GROUP_SHORTHAND(Padding) +CSS_PROP_LOGICAL_GROUP_AXIS(MinSize) +CSS_PROP_LOGICAL_GROUP_AXIS(Size) diff --git a/layout/style/nsCSSPropertyID.h b/layout/style/nsCSSPropertyID.h new file mode 100644 index 000000000..19f254006 --- /dev/null +++ b/layout/style/nsCSSPropertyID.h @@ -0,0 +1,118 @@ +/* -*- 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/. */ + +/* 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 nsCSSPropList.h + + */ +enum nsCSSPropertyID { + eCSSProperty_UNKNOWN = -1, + + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + eCSSProperty_##id_, + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP + + eCSSProperty_COUNT_no_shorthands, + // Make the count continue where it left off: + eCSSProperty_COUNT_DUMMY = eCSSProperty_COUNT_no_shorthands - 1, + + #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \ + eCSSProperty_##id_, + #include "nsCSSPropList.h" + #undef CSS_PROP_SHORTHAND + + eCSSProperty_COUNT, + // Make the count continue where it left off: + eCSSProperty_COUNT_DUMMY2 = eCSSProperty_COUNT - 1, + + #define CSS_PROP_ALIAS(aliasname_, id_, method_, pref_) \ + eCSSPropertyAlias_##method_, + #include "nsCSSPropAliasList.h" + #undef CSS_PROP_ALIAS + + eCSSProperty_COUNT_with_aliases, + // Make the count continue where it left off: + eCSSProperty_COUNT_DUMMY3 = eCSSProperty_COUNT_with_aliases - 1, + + // Some of the values below could probably overlap with each other + // if we had a need for them to do so. + + // Extra values for use in the values of the 'transition-property' + // property. + eCSSPropertyExtra_no_properties, + eCSSPropertyExtra_all_properties, + + // Extra dummy values for nsCSSParser internal use. + eCSSPropertyExtra_x_none_value, + eCSSPropertyExtra_x_auto_value, + + // Extra value to represent custom properties (--*). + eCSSPropertyExtra_variable, + + // Extra value for use in the DOM API's + eCSSProperty_DOM +}; + +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 +}; + +enum nsCSSPropertyLogicalGroup { + eCSSPropertyLogicalGroup_UNKNOWN = -1, +#define CSS_PROP_LOGICAL_GROUP_AXIS(name_) \ + eCSSPropertyLogicalGroup_##name_, +#define CSS_PROP_LOGICAL_GROUP_BOX(name_) \ + eCSSPropertyLogicalGroup_##name_, +#define CSS_PROP_LOGICAL_GROUP_SHORTHAND(name_) \ + eCSSPropertyLogicalGroup_##name_, +#include "nsCSSPropLogicalGroupList.h" +#undef CSS_PROP_LOGICAL_GROUP_SHORTHAND +#undef CSS_PROP_LOGICAL_GROUP_BOX +#undef CSS_PROP_LOGICAL_GROUP_AXIS + eCSSPropertyLogicalGroup_COUNT +}; + +#endif /* nsCSSPropertyID_h___ */ diff --git a/layout/style/nsCSSPropertyIDSet.h b/layout/style/nsCSSPropertyIDSet.h new file mode 100644 index 000000000..4de0471d1 --- /dev/null +++ b/layout/style/nsCSSPropertyIDSet.h @@ -0,0 +1,107 @@ +/* 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 "mozilla/ArrayUtils.h" +#include "mozilla/PodOperations.h" + +#include "nsCSSPropertyID.h" +#include // for CHAR_BIT + +/** + * 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: + nsCSSPropertyIDSet() { Empty(); } + // auto-generated copy-constructor OK + + void AssertInSetRange(nsCSSPropertyID aProperty) const { + NS_ASSERTION(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(nsCSSPropertyID aProperty) const { + AssertInSetRange(aProperty); + size_t p = aProperty; + return (mProperties[p / kBitsInChunk] & + (property_set_type(1) << (p % kBitsInChunk))) != 0; + } + + 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::PodEqual(mProperties, aOther.mProperties); + } + + // Return a new nsCSSPropertyIDSet which is the inverse of this set. + nsCSSPropertyIDSet Invert() const { + nsCSSPropertyIDSet result; + for (size_t i = 0; i < mozilla::ArrayLength(mProperties); ++i) { + result.mProperties[i] = ~mProperties[i]; + } + return result; + } + +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); + } + +private: + property_set_type mProperties[kChunkCount]; +}; + +#endif /* !defined(nsCSSPropertyIDSet_h__) */ diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp new file mode 100644 index 000000000..ec28d06f8 --- /dev/null +++ b/layout/style/nsCSSProps.cpp @@ -0,0 +1,3428 @@ +/* -*- 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/. */ + +/* + * methods for dealing with CSS properties and tables of the keyword + * values they accept + */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCSSProps.h" +#include "nsCSSKeywords.h" +#include "nsLayoutUtils.h" +#include "nsStyleConsts.h" +#include "nsIWidget.h" +#include "nsThemeConstants.h" // For system widget appearance types + +#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection +#include "mozilla/LookAndFeel.h" // for system colors + +#include "nsString.h" +#include "nsStaticAtom.h" +#include "nsStaticNameTable.h" + +#include "mozilla/Preferences.h" + +using namespace mozilla; + +typedef nsCSSProps::KTableEntry KTableEntry; + +// By wrapping internal-only properties in this macro, we are not +// exposing them in the CSSOM. Since currently it is not necessary to +// allow accessing them in that way, it is easier and cheaper to just +// do this rather than exposing them conditionally. +#define CSS_PROP(name_, id_, method_, flags_, pref_, ...) \ + static_assert(!((flags_) & CSS_PROPERTY_ENABLED_MASK) || pref_[0], \ + "Internal-only property '" #name_ "' should be wrapped in " \ + "#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL"); +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#define CSS_PROP_LIST_EXCLUDE_INTERNAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_EXCLUDE_INTERNAL +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP + +#define CSS_PROP(name_, id_, method_, flags_, pref_, ...) \ + static_assert(!((flags_) & CSS_PROPERTY_ENABLED_IN_CHROME) || \ + ((flags_) & CSS_PROPERTY_ENABLED_IN_UA_SHEETS), \ + "Property '" #name_ "' is enabled in chrome, so it should " \ + "also be enabled in UA sheets"); +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP + +// required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it +extern const char* const kCSSRawProperties[]; + +// define an array of all CSS properties +const char* const kCSSRawProperties[eCSSProperty_COUNT_with_aliases] = { +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + #name_, +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) #name_, +#include "nsCSSPropList.h" +#undef CSS_PROP_SHORTHAND +#define CSS_PROP_ALIAS(aliasname_, id_, method_, pref_) #aliasname_, +#include "nsCSSPropAliasList.h" +#undef CSS_PROP_ALIAS +}; + +using namespace mozilla; + +static int32_t gPropertyTableRefCount; +static nsStaticCaseInsensitiveNameTable* gPropertyTable; +static nsStaticCaseInsensitiveNameTable* gFontDescTable; +static nsStaticCaseInsensitiveNameTable* gCounterDescTable; +static nsStaticCaseInsensitiveNameTable* gPredefinedCounterStyleTable; +static nsDataHashtable* gPropertyIDLNameTable; + +/* static */ nsCSSPropertyID * + nsCSSProps::gShorthandsContainingTable[eCSSProperty_COUNT_no_shorthands]; +/* static */ nsCSSPropertyID* nsCSSProps::gShorthandsContainingPool = nullptr; + +static const char* const kCSSRawFontDescs[] = { +#define CSS_FONT_DESC(name_, method_) #name_, +#include "nsCSSFontDescList.h" +#undef CSS_FONT_DESC +}; + +static const char* const kCSSRawCounterDescs[] = { +#define CSS_COUNTER_DESC(name_, method_) #name_, +#include "nsCSSCounterDescList.h" +#undef CSS_COUNTER_DESC +}; + +static const char* const kCSSRawPredefinedCounterStyles[] = { + "none", + // 6 Simple Predefined Counter Styles + // 6.1 Numeric + "decimal", "decimal-leading-zero", "arabic-indic", "armenian", + "upper-armenian", "lower-armenian", "bengali", "cambodian", "khmer", + "cjk-decimal", "devanagari", "georgian", "gujarati", "gurmukhi", "hebrew", + "kannada", "lao", "malayalam", "mongolian", "myanmar", "oriya", "persian", + "lower-roman", "upper-roman", "tamil", "telugu", "thai", "tibetan", + // 6.2 Alphabetic + "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", + "cjk-earthly-branch", "cjk-heavenly-stem", "lower-greek", + "hiragana", "hiragana-iroha", "katakana", "katakana-iroha", + // 6.3 Symbolic + "disc", "circle", "square", "disclosure-open", "disclosure-closed", + // 7 Complex Predefined Counter Styles + // 7.1 Longhand East Asian Counter Styles + // 7.1.1 Japanese + "japanese-informal", "japanese-formal", + // 7.1.2 Korean + "korean-hangul-formal", "korean-hanja-informal", "korean-hanja-formal", + // 7.1.3 Chinese + "simp-chinese-informal", "simp-chinese-formal", + "trad-chinese-informal", "trad-chinese-formal", "cjk-ideographic", + // 7.2 Ethiopic Numeric Counter Style + "ethiopic-numeric" +}; + +struct PropertyAndCount { + nsCSSPropertyID property; + uint32_t count; +}; + +static int +SortPropertyAndCount(const void* s1, const void* s2, void *closure) +{ + const PropertyAndCount *pc1 = static_cast(s1); + const PropertyAndCount *pc2 = static_cast(s2); + // Primary sort by count (lowest to highest) + if (pc1->count != pc2->count) + return pc1->count - pc2->count; + // Secondary sort by property index (highest to lowest) + return pc2->property - pc1->property; +} + +// We need eCSSAliasCount so we can make gAliases nonzero size when there +// are no aliases. +enum { + eCSSAliasCount = eCSSProperty_COUNT_with_aliases - eCSSProperty_COUNT +}; + +// The names are in kCSSRawProperties. +static nsCSSPropertyID gAliases[eCSSAliasCount != 0 ? eCSSAliasCount : 1] = { +#define CSS_PROP_ALIAS(aliasname_, propid_, aliasmethod_, pref_) \ + eCSSProperty_##propid_ , +#include "nsCSSPropAliasList.h" +#undef CSS_PROP_ALIAS +}; + +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::AddRefTable(void) +{ + if (0 == gPropertyTableRefCount++) { + MOZ_ASSERT(!gPropertyTable, "pre existing array!"); + MOZ_ASSERT(!gFontDescTable, "pre existing array!"); + MOZ_ASSERT(!gCounterDescTable, "pre existing array!"); + MOZ_ASSERT(!gPredefinedCounterStyleTable, "pre existing array!"); + MOZ_ASSERT(!gPropertyIDLNameTable, "pre existing array!"); + + gPropertyTable = CreateStaticTable( + kCSSRawProperties, eCSSProperty_COUNT_with_aliases); + gFontDescTable = CreateStaticTable(kCSSRawFontDescs, eCSSFontDesc_COUNT); + gCounterDescTable = CreateStaticTable( + kCSSRawCounterDescs, eCSSCounterDesc_COUNT); + gPredefinedCounterStyleTable = CreateStaticTable( + kCSSRawPredefinedCounterStyles, + ArrayLength(kCSSRawPredefinedCounterStyles)); + + gPropertyIDLNameTable = new nsDataHashtable; + for (nsCSSPropertyID p = nsCSSPropertyID(0); + size_t(p) < ArrayLength(kIDLNameTable); + p = nsCSSPropertyID(p + 1)) { + if (kIDLNameTable[p]) { + gPropertyIDLNameTable->Put(nsDependentCString(kIDLNameTable[p]), p); + } + } + + BuildShorthandsContainingTable(); + + static bool prefObserversInited = false; + if (!prefObserversInited) { + prefObserversInited = true; + + #define OBSERVE_PROP(pref_, id_) \ + if (pref_[0]) { \ + Preferences::AddBoolVarCache(&gPropertyEnabled[id_], \ + pref_); \ + } + + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + OBSERVE_PROP(pref_, eCSSProperty_##id_) + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP + + #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \ + OBSERVE_PROP(pref_, eCSSProperty_##id_) + #include "nsCSSPropList.h" + #undef CSS_PROP_SHORTHAND + + #define CSS_PROP_ALIAS(aliasname_, propid_, aliasmethod_, pref_) \ + OBSERVE_PROP(pref_, eCSSPropertyAlias_##aliasmethod_) + #include "nsCSSPropAliasList.h" + #undef CSS_PROP_ALIAS + + #undef OBSERVE_PROP + } + +#ifdef DEBUG + { + // Assert that if CSS_PROPERTY_ENABLED_IN_UA_SHEETS or + // CSS_PROPERTY_ENABLED_IN_CHROME is used on a shorthand property + // that all of its component longhands also have the flag. + static uint32_t flagsToCheck[] = { + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_CHROME + }; + for (nsCSSPropertyID shorthand = eCSSProperty_COUNT_no_shorthands; + shorthand < eCSSProperty_COUNT; + shorthand = nsCSSPropertyID(shorthand + 1)) { + for (size_t i = 0; i < ArrayLength(flagsToCheck); i++) { + uint32_t flag = flagsToCheck[i]; + if (!nsCSSProps::PropHasFlags(shorthand, flag)) { + continue; + } + for (const nsCSSPropertyID* p = + nsCSSProps::SubpropertyEntryFor(shorthand); + *p != eCSSProperty_UNKNOWN; + ++p) { + MOZ_ASSERT(nsCSSProps::PropHasFlags(*p, flag), + "all subproperties of a property with a " + "CSS_PROPERTY_ENABLED_* flag must also have " + "the flag"); + } + } + } + + // Assert that CSS_PROPERTY_INTERNAL is used on properties in + // #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL sections of nsCSSPropList.h + // and on no others. + static nsCSSPropertyID nonInternalProperties[] = { + #define CSS_PROP(name_, id_, ...) eCSSProperty_##id_, + #define CSS_PROP_SHORTHAND(name_, id_, ...) eCSSProperty_##id_, + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #define CSS_PROP_LIST_EXCLUDE_INTERNAL + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST_EXCLUDE_INTERNAL + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP_SHORTHAND + #undef CSS_PROP + }; + MOZ_ASSERT(ArrayLength(nonInternalProperties) <= eCSSProperty_COUNT); + + bool found[eCSSProperty_COUNT]; + PodArrayZero(found); + for (nsCSSPropertyID p : nonInternalProperties) { + MOZ_ASSERT(!nsCSSProps::PropHasFlags(p, CSS_PROPERTY_INTERNAL), + "properties defined outside of #ifndef " + "CSS_PROP_LIST_EXCLUDE_INTERNAL sections must not have " + "the CSS_PROPERTY_INTERNAL flag"); + found[p] = true; + } + + for (size_t i = 0; i < ArrayLength(found); ++i) { + if (!found[i]) { + auto p = static_cast(i); + MOZ_ASSERT(nsCSSProps::PropHasFlags(p, CSS_PROPERTY_INTERNAL), + "properties defined in #ifndef " + "CSS_PROP_LIST_EXCLUDE_INTERNAL sections must have " + "the CSS_PROPERTY_INTERNAL flag"); + } + } + } +#endif + } +} + +#undef DEBUG_SHORTHANDS_CONTAINING + +bool +nsCSSProps::BuildShorthandsContainingTable() +{ + uint32_t occurrenceCounts[eCSSProperty_COUNT_no_shorthands]; + memset(occurrenceCounts, 0, sizeof(occurrenceCounts)); + PropertyAndCount subpropCounts[eCSSProperty_COUNT - + eCSSProperty_COUNT_no_shorthands]; + for (nsCSSPropertyID shorthand = eCSSProperty_COUNT_no_shorthands; + shorthand < eCSSProperty_COUNT; + shorthand = nsCSSPropertyID(shorthand + 1)) { +#ifdef DEBUG_SHORTHANDS_CONTAINING + printf("Considering shorthand property '%s'.\n", + nsCSSProps::GetStringValue(shorthand).get()); +#endif + PropertyAndCount &subpropCountsEntry = + subpropCounts[shorthand - eCSSProperty_COUNT_no_shorthands]; + subpropCountsEntry.property = shorthand; + subpropCountsEntry.count = 0; + if (nsCSSProps::PropHasFlags(shorthand, CSS_PROPERTY_IS_ALIAS)) { + // Don't put shorthands that are acting as aliases in the + // shorthands-containing lists. + continue; + } + for (const nsCSSPropertyID* subprops = SubpropertyEntryFor(shorthand); + *subprops != eCSSProperty_UNKNOWN; + ++subprops) { + MOZ_ASSERT(0 <= *subprops && *subprops < eCSSProperty_COUNT_no_shorthands, + "subproperty must be a longhand"); + ++occurrenceCounts[*subprops]; + ++subpropCountsEntry.count; + } + } + + uint32_t poolEntries = 0; + for (nsCSSPropertyID longhand = nsCSSPropertyID(0); + longhand < eCSSProperty_COUNT_no_shorthands; + longhand = nsCSSPropertyID(longhand + 1)) { + uint32_t count = occurrenceCounts[longhand]; + if (count > 0) + // leave room for terminator + poolEntries += count + 1; + } + + gShorthandsContainingPool = new nsCSSPropertyID[poolEntries]; + if (!gShorthandsContainingPool) + return false; + + // Initialize all entries to point to their null-terminator. + { + nsCSSPropertyID *poolCursor = gShorthandsContainingPool - 1; + nsCSSPropertyID *lastTerminator = + gShorthandsContainingPool + poolEntries - 1; + for (nsCSSPropertyID longhand = nsCSSPropertyID(0); + longhand < eCSSProperty_COUNT_no_shorthands; + longhand = nsCSSPropertyID(longhand + 1)) { + uint32_t count = occurrenceCounts[longhand]; + if (count > 0) { + poolCursor += count + 1; + gShorthandsContainingTable[longhand] = poolCursor; + *poolCursor = eCSSProperty_UNKNOWN; + } else { + gShorthandsContainingTable[longhand] = lastTerminator; + } + } + MOZ_ASSERT(poolCursor == lastTerminator, "miscalculation"); + } + + // Sort with lowest count at the start and highest at the end, and + // within counts sort in reverse property index order. + NS_QuickSort(&subpropCounts, ArrayLength(subpropCounts), + sizeof(subpropCounts[0]), SortPropertyAndCount, nullptr); + + // Fill in all the entries in gShorthandsContainingTable + for (const PropertyAndCount *shorthandAndCount = subpropCounts, + *shorthandAndCountEnd = ArrayEnd(subpropCounts); + shorthandAndCount < shorthandAndCountEnd; + ++shorthandAndCount) { +#ifdef DEBUG_SHORTHANDS_CONTAINING + printf("Entering %u subprops for '%s'.\n", + shorthandAndCount->count, + nsCSSProps::GetStringValue(shorthandAndCount->property).get()); +#endif + if (nsCSSProps::PropHasFlags(shorthandAndCount->property, + CSS_PROPERTY_IS_ALIAS)) { + // Don't put shorthands that are acting as aliases in the + // shorthands-containing lists. + continue; + } + for (const nsCSSPropertyID* subprops = + SubpropertyEntryFor(shorthandAndCount->property); + *subprops != eCSSProperty_UNKNOWN; + ++subprops) { + *(--gShorthandsContainingTable[*subprops]) = shorthandAndCount->property; + } + } + +#ifdef DEBUG_SHORTHANDS_CONTAINING + for (nsCSSPropertyID longhand = nsCSSPropertyID(0); + longhand < eCSSProperty_COUNT_no_shorthands; + longhand = nsCSSPropertyID(longhand + 1)) { + printf("Property %s is in %d shorthands.\n", + nsCSSProps::GetStringValue(longhand).get(), + occurrenceCounts[longhand]); + for (const nsCSSPropertyID *shorthands = ShorthandsContaining(longhand); + *shorthands != eCSSProperty_UNKNOWN; + ++shorthands) { + printf(" %s\n", nsCSSProps::GetStringValue(*shorthands).get()); + } + } +#endif + +#ifdef DEBUG + // Verify that all values that should be are present. + for (nsCSSPropertyID shorthand = eCSSProperty_COUNT_no_shorthands; + shorthand < eCSSProperty_COUNT; + shorthand = nsCSSPropertyID(shorthand + 1)) { + if (nsCSSProps::PropHasFlags(shorthand, CSS_PROPERTY_IS_ALIAS)) { + // Don't put shorthands that are acting as aliases in the + // shorthands-containing lists. + continue; + } + for (const nsCSSPropertyID* subprops = SubpropertyEntryFor(shorthand); + *subprops != eCSSProperty_UNKNOWN; + ++subprops) { + uint32_t count = 0; + for (const nsCSSPropertyID *shcont = ShorthandsContaining(*subprops); + *shcont != eCSSProperty_UNKNOWN; + ++shcont) { + if (*shcont == shorthand) + ++count; + } + MOZ_ASSERT(count == 1, + "subproperty of shorthand should have shorthand" + " in its ShorthandsContaining() table"); + } + } + + // Verify that there are no extra values + for (nsCSSPropertyID longhand = nsCSSPropertyID(0); + longhand < eCSSProperty_COUNT_no_shorthands; + longhand = nsCSSPropertyID(longhand + 1)) { + for (const nsCSSPropertyID *shorthands = ShorthandsContaining(longhand); + *shorthands != eCSSProperty_UNKNOWN; + ++shorthands) { + uint32_t count = 0; + for (const nsCSSPropertyID* subprops = SubpropertyEntryFor(*shorthands); + *subprops != eCSSProperty_UNKNOWN; + ++subprops) { + if (*subprops == longhand) + ++count; + } + MOZ_ASSERT(count == 1, + "longhand should be in subproperty table of " + "property in its ShorthandsContaining() table"); + } + } +#endif + + return true; +} + +void +nsCSSProps::ReleaseTable(void) +{ + if (0 == --gPropertyTableRefCount) { + delete gPropertyTable; + gPropertyTable = nullptr; + + delete gFontDescTable; + gFontDescTable = nullptr; + + delete gCounterDescTable; + gCounterDescTable = nullptr; + + delete gPredefinedCounterStyleTable; + gPredefinedCounterStyleTable = nullptr; + + delete gPropertyIDLNameTable; + gPropertyIDLNameTable = nullptr; + + delete [] gShorthandsContainingPool; + gShorthandsContainingPool = nullptr; + } +} + +/* static */ bool +nsCSSProps::IsInherited(nsCSSPropertyID aProperty) +{ + MOZ_ASSERT(!IsShorthand(aProperty)); + + nsStyleStructID sid = kSIDTable[aProperty]; + return nsCachedStyleData::IsInherited(sid); +} + +/* static */ bool +nsCSSProps::IsCustomPropertyName(const nsACString& aProperty) +{ + // Custom properties don't need to have a character after the "--" prefix. + return aProperty.Length() >= CSS_CUSTOM_NAME_PREFIX_LENGTH && + StringBeginsWith(aProperty, NS_LITERAL_CSTRING("--")); +} + +/* static */ bool +nsCSSProps::IsCustomPropertyName(const nsAString& aProperty) +{ + return aProperty.Length() >= CSS_CUSTOM_NAME_PREFIX_LENGTH && + StringBeginsWith(aProperty, NS_LITERAL_STRING("--")); +} + +nsCSSPropertyID +nsCSSProps::LookupProperty(const nsACString& aProperty, + EnabledState aEnabled) +{ + MOZ_ASSERT(gPropertyTable, "no lookup table, needs addref"); + + if (nsLayoutUtils::CSSVariablesEnabled() && + IsCustomPropertyName(aProperty)) { + return eCSSPropertyExtra_variable; + } + + nsCSSPropertyID res = nsCSSPropertyID(gPropertyTable->Lookup(aProperty)); + if (MOZ_LIKELY(res < eCSSProperty_COUNT)) { + if (res != eCSSProperty_UNKNOWN && !IsEnabled(res, aEnabled)) { + res = eCSSProperty_UNKNOWN; + } + return res; + } + MOZ_ASSERT(eCSSAliasCount != 0, + "'res' must be an alias at this point so we better have some!"); + // We intentionally don't support CSSEnabledState::eInUASheets or + // CSSEnabledState::eInChrome for aliases yet because it's unlikely + // there will be a need for it. + if (IsEnabled(res) || aEnabled == CSSEnabledState::eIgnoreEnabledState) { + res = gAliases[res - eCSSProperty_COUNT]; + MOZ_ASSERT(0 <= res && res < eCSSProperty_COUNT, + "aliases must not point to other aliases"); + if (IsEnabled(res) || aEnabled == CSSEnabledState::eIgnoreEnabledState) { + return res; + } + } + return eCSSProperty_UNKNOWN; +} + +nsCSSPropertyID +nsCSSProps::LookupProperty(const nsAString& aProperty, EnabledState aEnabled) +{ + if (nsLayoutUtils::CSSVariablesEnabled() && + IsCustomPropertyName(aProperty)) { + return eCSSPropertyExtra_variable; + } + + // This is faster than converting and calling + // LookupProperty(nsACString&). The table will do its own + // converting and avoid a PromiseFlatCString() call. + MOZ_ASSERT(gPropertyTable, "no lookup table, needs addref"); + nsCSSPropertyID res = nsCSSPropertyID(gPropertyTable->Lookup(aProperty)); + if (MOZ_LIKELY(res < eCSSProperty_COUNT)) { + if (res != eCSSProperty_UNKNOWN && !IsEnabled(res, aEnabled)) { + res = eCSSProperty_UNKNOWN; + } + return res; + } + MOZ_ASSERT(eCSSAliasCount != 0, + "'res' must be an alias at this point so we better have some!"); + // We intentionally don't support CSSEnabledState::eInUASheets or + // CSSEnabledState::eInChrome for aliases yet because it's unlikely + // there will be a need for it. + if (IsEnabled(res) || aEnabled == CSSEnabledState::eIgnoreEnabledState) { + res = gAliases[res - eCSSProperty_COUNT]; + MOZ_ASSERT(0 <= res && res < eCSSProperty_COUNT, + "aliases must not point to other aliases"); + if (IsEnabled(res) || aEnabled == CSSEnabledState::eIgnoreEnabledState) { + return res; + } + } + return eCSSProperty_UNKNOWN; +} + +nsCSSPropertyID +nsCSSProps::LookupPropertyByIDLName(const nsACString& aPropertyIDLName, + EnabledState aEnabled) +{ + nsCSSPropertyID res; + if (!gPropertyIDLNameTable->Get(aPropertyIDLName, &res)) { + return eCSSProperty_UNKNOWN; + } + MOZ_ASSERT(res < eCSSProperty_COUNT); + if (!IsEnabled(res, aEnabled)) { + return eCSSProperty_UNKNOWN; + } + return res; +} + +nsCSSPropertyID +nsCSSProps::LookupPropertyByIDLName(const nsAString& aPropertyIDLName, + EnabledState aEnabled) +{ + MOZ_ASSERT(gPropertyIDLNameTable, "no lookup table, needs addref"); + return LookupPropertyByIDLName(NS_ConvertUTF16toUTF8(aPropertyIDLName), + aEnabled); +} + +nsCSSFontDesc +nsCSSProps::LookupFontDesc(const nsACString& aFontDesc) +{ + MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref"); + nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc)); + + if (which == eCSSFontDesc_Display && + !Preferences::GetBool("layout.css.font-display.enabled")) { + which = eCSSFontDesc_UNKNOWN; + } else if (which == eCSSFontDesc_UNKNOWN) { + // check for unprefixed font-feature-settings/font-language-override + nsAutoCString prefixedProp; + prefixedProp.AppendLiteral("-moz-"); + prefixedProp.Append(aFontDesc); + which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp)); + } + return which; +} + +nsCSSFontDesc +nsCSSProps::LookupFontDesc(const nsAString& aFontDesc) +{ + MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref"); + nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc)); + + if (which == eCSSFontDesc_Display && + !Preferences::GetBool("layout.css.font-display.enabled")) { + which = eCSSFontDesc_UNKNOWN; + } else if (which == eCSSFontDesc_UNKNOWN) { + // check for unprefixed font-feature-settings/font-language-override + nsAutoString prefixedProp; + prefixedProp.AppendLiteral("-moz-"); + prefixedProp.Append(aFontDesc); + which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp)); + } + return which; +} + +nsCSSCounterDesc +nsCSSProps::LookupCounterDesc(const nsAString& aProperty) +{ + MOZ_ASSERT(gCounterDescTable, "no lookup table, needs addref"); + return nsCSSCounterDesc(gCounterDescTable->Lookup(aProperty)); +} + +nsCSSCounterDesc +nsCSSProps::LookupCounterDesc(const nsACString& aProperty) +{ + MOZ_ASSERT(gCounterDescTable, "no lookup table, needs addref"); + return nsCSSCounterDesc(gCounterDescTable->Lookup(aProperty)); +} + +bool +nsCSSProps::IsPredefinedCounterStyle(const nsAString& aStyle) +{ + MOZ_ASSERT(gPredefinedCounterStyleTable, + "no lookup table, needs addref"); + return gPredefinedCounterStyleTable->Lookup(aStyle) != + nsStaticCaseInsensitiveNameTable::NOT_FOUND; +} + +bool +nsCSSProps::IsPredefinedCounterStyle(const nsACString& aStyle) +{ + MOZ_ASSERT(gPredefinedCounterStyleTable, + "no lookup table, needs addref"); + return gPredefinedCounterStyleTable->Lookup(aStyle) != + nsStaticCaseInsensitiveNameTable::NOT_FOUND; +} + +const nsAFlatCString& +nsCSSProps::GetStringValue(nsCSSPropertyID aProperty) +{ + MOZ_ASSERT(gPropertyTable, "no lookup table, needs addref"); + if (gPropertyTable) { + return gPropertyTable->GetStringValue(int32_t(aProperty)); + } else { + static nsDependentCString sNullStr(""); + return sNullStr; + } +} + +const nsAFlatCString& +nsCSSProps::GetStringValue(nsCSSFontDesc aFontDescID) +{ + MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref"); + if (gFontDescTable) { + return gFontDescTable->GetStringValue(int32_t(aFontDescID)); + } else { + static nsDependentCString sNullStr(""); + return sNullStr; + } +} + +const nsAFlatCString& +nsCSSProps::GetStringValue(nsCSSCounterDesc aCounterDesc) +{ + MOZ_ASSERT(gCounterDescTable, "no lookup table, needs addref"); + if (gCounterDescTable) { + return gCounterDescTable->GetStringValue(int32_t(aCounterDesc)); + } else { + static nsDependentCString sNullStr(""); + return sNullStr; + } +} + +#define CSS_PROP(name_, id_, ...) nsICSSProperty* nsCSSProps::id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +#define CSS_PROP(name_, id_, ...) NS_STATIC_ATOM_BUFFER(id_##_buffer, #name_) +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +static const nsStaticAtom CSSProps_info[] = { +#define CSS_PROP(name_, id_, ...) \ + NS_STATIC_ATOM(id_##_buffer, (nsIAtom**)&nsCSSProps::id_), +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, __VA_ARGS__) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP +}; + +nsICSSProperty* nsCSSProps::gPropertyAtomTable[eCSSProperty_COUNT]; + +/* static */ void +nsCSSProps::AddRefAtoms() +{ + NS_RegisterStaticAtoms(CSSProps_info); +#define CSS_PROP(name_, id_, ...) \ + gPropertyAtomTable[eCSSProperty_##id_] = nsCSSProps::id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP +} + +/***************************************************************************/ + +const KTableEntry nsCSSProps::kAnimationDirectionKTable[] = { + { eCSSKeyword_normal, static_cast(dom::PlaybackDirection::Normal) }, + { eCSSKeyword_reverse, static_cast(dom::PlaybackDirection::Reverse) }, + { eCSSKeyword_alternate, static_cast(dom::PlaybackDirection::Alternate) }, + { eCSSKeyword_alternate_reverse, static_cast(dom::PlaybackDirection::Alternate_reverse) }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAnimationFillModeKTable[] = { + { eCSSKeyword_none, static_cast(dom::FillMode::None) }, + { eCSSKeyword_forwards, static_cast(dom::FillMode::Forwards) }, + { eCSSKeyword_backwards, static_cast(dom::FillMode::Backwards) }, + { eCSSKeyword_both, static_cast(dom::FillMode::Both) }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAnimationIterationCountKTable[] = { + { eCSSKeyword_infinite, NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAnimationPlayStateKTable[] = { + { eCSSKeyword_running, NS_STYLE_ANIMATION_PLAY_STATE_RUNNING }, + { eCSSKeyword_paused, NS_STYLE_ANIMATION_PLAY_STATE_PAUSED }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAppearanceKTable[] = { + { eCSSKeyword_none, NS_THEME_NONE }, + { eCSSKeyword_button, NS_THEME_BUTTON }, + { eCSSKeyword_radio, NS_THEME_RADIO }, + { eCSSKeyword_checkbox, NS_THEME_CHECKBOX }, + { eCSSKeyword_button_bevel, NS_THEME_BUTTON_BEVEL }, + { eCSSKeyword_toolbox, NS_THEME_TOOLBOX }, + { eCSSKeyword_toolbar, NS_THEME_TOOLBAR }, + { eCSSKeyword_toolbarbutton, NS_THEME_TOOLBARBUTTON }, + { eCSSKeyword_toolbargripper, NS_THEME_TOOLBARGRIPPER }, + { eCSSKeyword_dualbutton, NS_THEME_DUALBUTTON }, + { eCSSKeyword_toolbarbutton_dropdown, NS_THEME_TOOLBARBUTTON_DROPDOWN }, + { eCSSKeyword_button_arrow_up, NS_THEME_BUTTON_ARROW_UP }, + { eCSSKeyword_button_arrow_down, NS_THEME_BUTTON_ARROW_DOWN }, + { eCSSKeyword_button_arrow_next, NS_THEME_BUTTON_ARROW_NEXT }, + { eCSSKeyword_button_arrow_previous, NS_THEME_BUTTON_ARROW_PREVIOUS }, + { eCSSKeyword_meterbar, NS_THEME_METERBAR }, + { eCSSKeyword_meterchunk, NS_THEME_METERCHUNK }, + { eCSSKeyword_number_input, NS_THEME_NUMBER_INPUT }, + { eCSSKeyword_separator, NS_THEME_SEPARATOR }, + { eCSSKeyword_splitter, NS_THEME_SPLITTER }, + { eCSSKeyword_statusbar, NS_THEME_STATUSBAR }, + { eCSSKeyword_statusbarpanel, NS_THEME_STATUSBARPANEL }, + { eCSSKeyword_resizerpanel, NS_THEME_RESIZERPANEL }, + { eCSSKeyword_resizer, NS_THEME_RESIZER }, + { eCSSKeyword_listbox, NS_THEME_LISTBOX }, + { eCSSKeyword_listitem, NS_THEME_LISTITEM }, + { eCSSKeyword_treeview, NS_THEME_TREEVIEW }, + { eCSSKeyword_treeitem, NS_THEME_TREEITEM }, + { eCSSKeyword_treetwisty, NS_THEME_TREETWISTY }, + { eCSSKeyword_treetwistyopen, NS_THEME_TREETWISTYOPEN }, + { eCSSKeyword_treeline, NS_THEME_TREELINE }, + { eCSSKeyword_treeheader, NS_THEME_TREEHEADER }, + { eCSSKeyword_treeheadercell, NS_THEME_TREEHEADERCELL }, + { eCSSKeyword_treeheadersortarrow, NS_THEME_TREEHEADERSORTARROW }, + { eCSSKeyword_progressbar, NS_THEME_PROGRESSBAR }, + { eCSSKeyword_progresschunk, NS_THEME_PROGRESSCHUNK }, + { eCSSKeyword_progressbar_vertical, NS_THEME_PROGRESSBAR_VERTICAL }, + { eCSSKeyword_progresschunk_vertical, NS_THEME_PROGRESSCHUNK_VERTICAL }, + { eCSSKeyword_tab, NS_THEME_TAB }, + { eCSSKeyword_tabpanels, NS_THEME_TABPANELS }, + { eCSSKeyword_tabpanel, NS_THEME_TABPANEL }, + { eCSSKeyword_tab_scroll_arrow_back, NS_THEME_TAB_SCROLL_ARROW_BACK }, + { eCSSKeyword_tab_scroll_arrow_forward, NS_THEME_TAB_SCROLL_ARROW_FORWARD }, + { eCSSKeyword_tooltip, NS_THEME_TOOLTIP }, + { eCSSKeyword_spinner, NS_THEME_SPINNER }, + { eCSSKeyword_spinner_upbutton, NS_THEME_SPINNER_UPBUTTON }, + { eCSSKeyword_spinner_downbutton, NS_THEME_SPINNER_DOWNBUTTON }, + { eCSSKeyword_spinner_textfield, NS_THEME_SPINNER_TEXTFIELD }, + { eCSSKeyword_scrollbar, NS_THEME_SCROLLBAR }, + { eCSSKeyword_scrollbar_small, NS_THEME_SCROLLBAR_SMALL }, + { eCSSKeyword_scrollbar_horizontal, NS_THEME_SCROLLBAR_HORIZONTAL }, + { eCSSKeyword_scrollbar_vertical, NS_THEME_SCROLLBAR_VERTICAL }, + { eCSSKeyword_scrollbarbutton_up, NS_THEME_SCROLLBARBUTTON_UP }, + { eCSSKeyword_scrollbarbutton_down, NS_THEME_SCROLLBARBUTTON_DOWN }, + { eCSSKeyword_scrollbarbutton_left, NS_THEME_SCROLLBARBUTTON_LEFT }, + { eCSSKeyword_scrollbarbutton_right, NS_THEME_SCROLLBARBUTTON_RIGHT }, + { eCSSKeyword_scrollbartrack_horizontal, NS_THEME_SCROLLBARTRACK_HORIZONTAL }, + { eCSSKeyword_scrollbartrack_vertical, NS_THEME_SCROLLBARTRACK_VERTICAL }, + { eCSSKeyword_scrollbarthumb_horizontal, NS_THEME_SCROLLBARTHUMB_HORIZONTAL }, + { eCSSKeyword_scrollbarthumb_vertical, NS_THEME_SCROLLBARTHUMB_VERTICAL }, + { eCSSKeyword_textfield, NS_THEME_TEXTFIELD }, + { eCSSKeyword_textfield_multiline, NS_THEME_TEXTFIELD_MULTILINE }, + { eCSSKeyword_caret, NS_THEME_CARET }, + { eCSSKeyword_searchfield, NS_THEME_SEARCHFIELD }, + { eCSSKeyword_menulist, NS_THEME_MENULIST }, + { eCSSKeyword_menulist_button, NS_THEME_MENULIST_BUTTON }, + { eCSSKeyword_menulist_text, NS_THEME_MENULIST_TEXT }, + { eCSSKeyword_menulist_textfield, NS_THEME_MENULIST_TEXTFIELD }, + { eCSSKeyword_range, NS_THEME_RANGE }, + { eCSSKeyword_range_thumb, NS_THEME_RANGE_THUMB }, + { eCSSKeyword_scale_horizontal, NS_THEME_SCALE_HORIZONTAL }, + { eCSSKeyword_scale_vertical, NS_THEME_SCALE_VERTICAL }, + { eCSSKeyword_scalethumb_horizontal, NS_THEME_SCALETHUMB_HORIZONTAL }, + { eCSSKeyword_scalethumb_vertical, NS_THEME_SCALETHUMB_VERTICAL }, + { eCSSKeyword_scalethumbstart, NS_THEME_SCALETHUMBSTART }, + { eCSSKeyword_scalethumbend, NS_THEME_SCALETHUMBEND }, + { eCSSKeyword_scalethumbtick, NS_THEME_SCALETHUMBTICK }, + { eCSSKeyword_groupbox, NS_THEME_GROUPBOX }, + { eCSSKeyword_checkbox_container, NS_THEME_CHECKBOX_CONTAINER }, + { eCSSKeyword_radio_container, NS_THEME_RADIO_CONTAINER }, + { eCSSKeyword_checkbox_label, NS_THEME_CHECKBOX_LABEL }, + { eCSSKeyword_radio_label, NS_THEME_RADIO_LABEL }, + { eCSSKeyword_button_focus, NS_THEME_BUTTON_FOCUS }, + { eCSSKeyword_window, NS_THEME_WINDOW }, + { eCSSKeyword_dialog, NS_THEME_DIALOG }, + { eCSSKeyword_menubar, NS_THEME_MENUBAR }, + { eCSSKeyword_menupopup, NS_THEME_MENUPOPUP }, + { eCSSKeyword_menuitem, NS_THEME_MENUITEM }, + { eCSSKeyword_checkmenuitem, NS_THEME_CHECKMENUITEM }, + { eCSSKeyword_radiomenuitem, NS_THEME_RADIOMENUITEM }, + { eCSSKeyword_menucheckbox, NS_THEME_MENUCHECKBOX }, + { eCSSKeyword_menuradio, NS_THEME_MENURADIO }, + { eCSSKeyword_menuseparator, NS_THEME_MENUSEPARATOR }, + { eCSSKeyword_menuarrow, NS_THEME_MENUARROW }, + { eCSSKeyword_menuimage, NS_THEME_MENUIMAGE }, + { eCSSKeyword_menuitemtext, NS_THEME_MENUITEMTEXT }, + { eCSSKeyword__moz_win_media_toolbox, NS_THEME_WIN_MEDIA_TOOLBOX }, + { eCSSKeyword__moz_win_communications_toolbox, NS_THEME_WIN_COMMUNICATIONS_TOOLBOX }, + { eCSSKeyword__moz_win_browsertabbar_toolbox, NS_THEME_WIN_BROWSERTABBAR_TOOLBOX }, + { eCSSKeyword__moz_win_glass, NS_THEME_WIN_GLASS }, + { eCSSKeyword__moz_win_borderless_glass, NS_THEME_WIN_BORDERLESS_GLASS }, + { eCSSKeyword__moz_mac_fullscreen_button, NS_THEME_MAC_FULLSCREEN_BUTTON }, + { eCSSKeyword__moz_mac_help_button, NS_THEME_MAC_HELP_BUTTON }, + { eCSSKeyword__moz_window_titlebar, NS_THEME_WINDOW_TITLEBAR }, + { eCSSKeyword__moz_window_titlebar_maximized, NS_THEME_WINDOW_TITLEBAR_MAXIMIZED }, + { eCSSKeyword__moz_window_frame_left, NS_THEME_WINDOW_FRAME_LEFT }, + { eCSSKeyword__moz_window_frame_right, NS_THEME_WINDOW_FRAME_RIGHT }, + { eCSSKeyword__moz_window_frame_bottom, NS_THEME_WINDOW_FRAME_BOTTOM }, + { eCSSKeyword__moz_window_button_close, NS_THEME_WINDOW_BUTTON_CLOSE }, + { eCSSKeyword__moz_window_button_minimize, NS_THEME_WINDOW_BUTTON_MINIMIZE }, + { eCSSKeyword__moz_window_button_maximize, NS_THEME_WINDOW_BUTTON_MAXIMIZE }, + { eCSSKeyword__moz_window_button_restore, NS_THEME_WINDOW_BUTTON_RESTORE }, + { eCSSKeyword__moz_window_button_box, NS_THEME_WINDOW_BUTTON_BOX }, + { eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED }, + { eCSSKeyword__moz_win_exclude_glass, NS_THEME_WIN_EXCLUDE_GLASS }, + { eCSSKeyword__moz_mac_vibrancy_light, NS_THEME_MAC_VIBRANCY_LIGHT }, + { eCSSKeyword__moz_mac_vibrancy_dark, NS_THEME_MAC_VIBRANCY_DARK }, + { eCSSKeyword__moz_mac_disclosure_button_open, NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN }, + { eCSSKeyword__moz_mac_disclosure_button_closed, NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED }, + { eCSSKeyword__moz_gtk_info_bar, NS_THEME_GTK_INFO_BAR }, + { eCSSKeyword__moz_mac_source_list, NS_THEME_MAC_SOURCE_LIST }, + { eCSSKeyword__moz_mac_source_list_selection, NS_THEME_MAC_SOURCE_LIST_SELECTION }, + { eCSSKeyword__moz_mac_active_source_list_selection, NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBackfaceVisibilityKTable[] = { + { eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE }, + { eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTransformStyleKTable[] = { + { eCSSKeyword_flat, NS_STYLE_TRANSFORM_STYLE_FLAT }, + { eCSSKeyword_preserve_3d, NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerAttachmentKTable[] = { + { eCSSKeyword_fixed, NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED }, + { eCSSKeyword_scroll, NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL }, + { eCSSKeyword_local, NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +static_assert(NS_STYLE_IMAGELAYER_CLIP_BORDER == NS_STYLE_IMAGELAYER_ORIGIN_BORDER && + NS_STYLE_IMAGELAYER_CLIP_PADDING == NS_STYLE_IMAGELAYER_ORIGIN_PADDING && + NS_STYLE_IMAGELAYER_CLIP_CONTENT == NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, + "Except background-clip:text, all {background,mask}-clip and " + "{background,mask}-origin style constants must agree"); + +const KTableEntry nsCSSProps::kImageLayerOriginKTable[] = { + { eCSSKeyword_border_box, NS_STYLE_IMAGELAYER_ORIGIN_BORDER }, + { eCSSKeyword_padding_box, NS_STYLE_IMAGELAYER_ORIGIN_PADDING }, + { eCSSKeyword_content_box, NS_STYLE_IMAGELAYER_ORIGIN_CONTENT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kBackgroundClipKTable[] = { + { eCSSKeyword_border_box, NS_STYLE_IMAGELAYER_CLIP_BORDER }, + { eCSSKeyword_padding_box, NS_STYLE_IMAGELAYER_CLIP_PADDING }, + { eCSSKeyword_content_box, NS_STYLE_IMAGELAYER_CLIP_CONTENT }, + // The next entry is controlled by the layout.css.background-clip-text.enabled + // pref. + { eCSSKeyword_text, NS_STYLE_IMAGELAYER_CLIP_TEXT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +static_assert(MOZ_ARRAY_LENGTH(nsCSSProps::kImageLayerOriginKTable) == + MOZ_ARRAY_LENGTH(nsCSSProps::kBackgroundClipKTable) - 1, + "background-clip has one extra value, which is text, compared" + "to {background,mask}-origin"); + +// Note: Don't change this table unless you update +// ParseImageLayerPosition! + +const KTableEntry nsCSSProps::kImageLayerPositionKTable[] = { + { eCSSKeyword_center, NS_STYLE_IMAGELAYER_POSITION_CENTER }, + { eCSSKeyword_top, NS_STYLE_IMAGELAYER_POSITION_TOP }, + { eCSSKeyword_bottom, NS_STYLE_IMAGELAYER_POSITION_BOTTOM }, + { eCSSKeyword_left, NS_STYLE_IMAGELAYER_POSITION_LEFT }, + { eCSSKeyword_right, NS_STYLE_IMAGELAYER_POSITION_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerRepeatKTable[] = { + { eCSSKeyword_no_repeat, NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT }, + { eCSSKeyword_repeat, NS_STYLE_IMAGELAYER_REPEAT_REPEAT }, + { eCSSKeyword_repeat_x, NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X }, + { eCSSKeyword_repeat_y, NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y }, + { eCSSKeyword_round, NS_STYLE_IMAGELAYER_REPEAT_ROUND}, + { eCSSKeyword_space, NS_STYLE_IMAGELAYER_REPEAT_SPACE}, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerRepeatPartKTable[] = { + { eCSSKeyword_no_repeat, NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT }, + { eCSSKeyword_repeat, NS_STYLE_IMAGELAYER_REPEAT_REPEAT }, + { eCSSKeyword_round, NS_STYLE_IMAGELAYER_REPEAT_ROUND}, + { eCSSKeyword_space, NS_STYLE_IMAGELAYER_REPEAT_SPACE}, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerSizeKTable[] = { + { eCSSKeyword_contain, NS_STYLE_IMAGELAYER_SIZE_CONTAIN }, + { eCSSKeyword_cover, NS_STYLE_IMAGELAYER_SIZE_COVER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerModeKTable[] = { + { eCSSKeyword_alpha, NS_STYLE_MASK_MODE_ALPHA }, + { eCSSKeyword_luminance, NS_STYLE_MASK_MODE_LUMINANCE }, + { eCSSKeyword_match_source, NS_STYLE_MASK_MODE_MATCH_SOURCE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageLayerCompositeKTable[] = { + { eCSSKeyword_add, NS_STYLE_MASK_COMPOSITE_ADD }, + { eCSSKeyword_subtract, NS_STYLE_MASK_COMPOSITE_SUBTRACT }, + { eCSSKeyword_intersect, NS_STYLE_MASK_COMPOSITE_INTERSECT }, + { eCSSKeyword_exclude, NS_STYLE_MASK_COMPOSITE_EXCLUDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBlendModeKTable[] = { + { eCSSKeyword_normal, NS_STYLE_BLEND_NORMAL }, + { eCSSKeyword_multiply, NS_STYLE_BLEND_MULTIPLY }, + { eCSSKeyword_screen, NS_STYLE_BLEND_SCREEN }, + { eCSSKeyword_overlay, NS_STYLE_BLEND_OVERLAY }, + { eCSSKeyword_darken, NS_STYLE_BLEND_DARKEN }, + { eCSSKeyword_lighten, NS_STYLE_BLEND_LIGHTEN }, + { eCSSKeyword_color_dodge, NS_STYLE_BLEND_COLOR_DODGE }, + { eCSSKeyword_color_burn, NS_STYLE_BLEND_COLOR_BURN }, + { eCSSKeyword_hard_light, NS_STYLE_BLEND_HARD_LIGHT }, + { eCSSKeyword_soft_light, NS_STYLE_BLEND_SOFT_LIGHT }, + { eCSSKeyword_difference, NS_STYLE_BLEND_DIFFERENCE }, + { eCSSKeyword_exclusion, NS_STYLE_BLEND_EXCLUSION }, + { eCSSKeyword_hue, NS_STYLE_BLEND_HUE }, + { eCSSKeyword_saturation, NS_STYLE_BLEND_SATURATION }, + { eCSSKeyword_color, NS_STYLE_BLEND_COLOR }, + { eCSSKeyword_luminosity, NS_STYLE_BLEND_LUMINOSITY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBorderCollapseKTable[] = { + { eCSSKeyword_collapse, NS_STYLE_BORDER_COLLAPSE }, + { eCSSKeyword_separate, NS_STYLE_BORDER_SEPARATE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBorderImageRepeatKTable[] = { + { eCSSKeyword_stretch, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH }, + { eCSSKeyword_repeat, NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT }, + { eCSSKeyword_round, NS_STYLE_BORDER_IMAGE_REPEAT_ROUND }, + { eCSSKeyword_space, NS_STYLE_BORDER_IMAGE_REPEAT_SPACE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBorderImageSliceKTable[] = { + { eCSSKeyword_fill, NS_STYLE_BORDER_IMAGE_SLICE_FILL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBorderStyleKTable[] = { + { eCSSKeyword_none, NS_STYLE_BORDER_STYLE_NONE }, + { eCSSKeyword_hidden, NS_STYLE_BORDER_STYLE_HIDDEN }, + { eCSSKeyword_dotted, NS_STYLE_BORDER_STYLE_DOTTED }, + { eCSSKeyword_dashed, NS_STYLE_BORDER_STYLE_DASHED }, + { eCSSKeyword_solid, NS_STYLE_BORDER_STYLE_SOLID }, + { eCSSKeyword_double, NS_STYLE_BORDER_STYLE_DOUBLE }, + { eCSSKeyword_groove, NS_STYLE_BORDER_STYLE_GROOVE }, + { eCSSKeyword_ridge, NS_STYLE_BORDER_STYLE_RIDGE }, + { eCSSKeyword_inset, NS_STYLE_BORDER_STYLE_INSET }, + { eCSSKeyword_outset, NS_STYLE_BORDER_STYLE_OUTSET }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBorderWidthKTable[] = { + { eCSSKeyword_thin, NS_STYLE_BORDER_WIDTH_THIN }, + { eCSSKeyword_medium, NS_STYLE_BORDER_WIDTH_MEDIUM }, + { eCSSKeyword_thick, NS_STYLE_BORDER_WIDTH_THICK }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxDecorationBreakKTable[] = { + { eCSSKeyword_slice, StyleBoxDecorationBreak::Slice }, + { eCSSKeyword_clone, StyleBoxDecorationBreak::Clone }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxShadowTypeKTable[] = { + { eCSSKeyword_inset, uint8_t(StyleBoxShadowType::Inset) }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxSizingKTable[] = { + { eCSSKeyword_content_box, StyleBoxSizing::Content }, + { eCSSKeyword_border_box, StyleBoxSizing::Border }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCaptionSideKTable[] = { + { eCSSKeyword_top, NS_STYLE_CAPTION_SIDE_TOP }, + { eCSSKeyword_right, NS_STYLE_CAPTION_SIDE_RIGHT }, + { eCSSKeyword_bottom, NS_STYLE_CAPTION_SIDE_BOTTOM }, + { eCSSKeyword_left, NS_STYLE_CAPTION_SIDE_LEFT }, + { eCSSKeyword_top_outside, NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE }, + { eCSSKeyword_bottom_outside, NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kClearKTable[] = { + { eCSSKeyword_none, StyleClear::None }, + { eCSSKeyword_left, StyleClear::Left }, + { eCSSKeyword_right, StyleClear::Right }, + { eCSSKeyword_inline_start, StyleClear::InlineStart }, + { eCSSKeyword_inline_end, StyleClear::InlineEnd }, + { eCSSKeyword_both, StyleClear::Both }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// See also kContextPatternKTable for SVG paint-specific values +const KTableEntry nsCSSProps::kColorKTable[] = { + { eCSSKeyword_activeborder, LookAndFeel::eColorID_activeborder }, + { eCSSKeyword_activecaption, LookAndFeel::eColorID_activecaption }, + { eCSSKeyword_appworkspace, LookAndFeel::eColorID_appworkspace }, + { eCSSKeyword_background, LookAndFeel::eColorID_background }, + { eCSSKeyword_buttonface, LookAndFeel::eColorID_buttonface }, + { eCSSKeyword_buttonhighlight, LookAndFeel::eColorID_buttonhighlight }, + { eCSSKeyword_buttonshadow, LookAndFeel::eColorID_buttonshadow }, + { eCSSKeyword_buttontext, LookAndFeel::eColorID_buttontext }, + { eCSSKeyword_captiontext, LookAndFeel::eColorID_captiontext }, + { eCSSKeyword_graytext, LookAndFeel::eColorID_graytext }, + { eCSSKeyword_highlight, LookAndFeel::eColorID_highlight }, + { eCSSKeyword_highlighttext, LookAndFeel::eColorID_highlighttext }, + { eCSSKeyword_inactiveborder, LookAndFeel::eColorID_inactiveborder }, + { eCSSKeyword_inactivecaption, LookAndFeel::eColorID_inactivecaption }, + { eCSSKeyword_inactivecaptiontext, LookAndFeel::eColorID_inactivecaptiontext }, + { eCSSKeyword_infobackground, LookAndFeel::eColorID_infobackground }, + { eCSSKeyword_infotext, LookAndFeel::eColorID_infotext }, + { eCSSKeyword_menu, LookAndFeel::eColorID_menu }, + { eCSSKeyword_menutext, LookAndFeel::eColorID_menutext }, + { eCSSKeyword_scrollbar, LookAndFeel::eColorID_scrollbar }, + { eCSSKeyword_threeddarkshadow, LookAndFeel::eColorID_threeddarkshadow }, + { eCSSKeyword_threedface, LookAndFeel::eColorID_threedface }, + { eCSSKeyword_threedhighlight, LookAndFeel::eColorID_threedhighlight }, + { eCSSKeyword_threedlightshadow, LookAndFeel::eColorID_threedlightshadow }, + { eCSSKeyword_threedshadow, LookAndFeel::eColorID_threedshadow }, + { eCSSKeyword_window, LookAndFeel::eColorID_window }, + { eCSSKeyword_windowframe, LookAndFeel::eColorID_windowframe }, + { eCSSKeyword_windowtext, LookAndFeel::eColorID_windowtext }, + { eCSSKeyword__moz_activehyperlinktext, NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT }, + { eCSSKeyword__moz_buttondefault, LookAndFeel::eColorID__moz_buttondefault }, + { eCSSKeyword__moz_buttonhoverface, LookAndFeel::eColorID__moz_buttonhoverface }, + { eCSSKeyword__moz_buttonhovertext, LookAndFeel::eColorID__moz_buttonhovertext }, + { eCSSKeyword__moz_cellhighlight, LookAndFeel::eColorID__moz_cellhighlight }, + { eCSSKeyword__moz_cellhighlighttext, LookAndFeel::eColorID__moz_cellhighlighttext }, + { eCSSKeyword__moz_eventreerow, LookAndFeel::eColorID__moz_eventreerow }, + { eCSSKeyword__moz_field, LookAndFeel::eColorID__moz_field }, + { eCSSKeyword__moz_fieldtext, LookAndFeel::eColorID__moz_fieldtext }, + { eCSSKeyword__moz_default_background_color, NS_COLOR_MOZ_DEFAULT_BACKGROUND_COLOR }, + { eCSSKeyword__moz_default_color, NS_COLOR_MOZ_DEFAULT_COLOR }, + { eCSSKeyword__moz_dialog, LookAndFeel::eColorID__moz_dialog }, + { eCSSKeyword__moz_dialogtext, LookAndFeel::eColorID__moz_dialogtext }, + { eCSSKeyword__moz_dragtargetzone, LookAndFeel::eColorID__moz_dragtargetzone }, + { eCSSKeyword__moz_gtk_info_bar_text, LookAndFeel::eColorID__moz_gtk_info_bar_text }, + { eCSSKeyword__moz_hyperlinktext, NS_COLOR_MOZ_HYPERLINKTEXT }, + { eCSSKeyword__moz_html_cellhighlight, LookAndFeel::eColorID__moz_html_cellhighlight }, + { eCSSKeyword__moz_html_cellhighlighttext, LookAndFeel::eColorID__moz_html_cellhighlighttext }, + { eCSSKeyword__moz_mac_buttonactivetext, LookAndFeel::eColorID__moz_mac_buttonactivetext }, + { eCSSKeyword__moz_mac_chrome_active, LookAndFeel::eColorID__moz_mac_chrome_active }, + { eCSSKeyword__moz_mac_chrome_inactive, LookAndFeel::eColorID__moz_mac_chrome_inactive }, + { eCSSKeyword__moz_mac_defaultbuttontext, LookAndFeel::eColorID__moz_mac_defaultbuttontext }, + { eCSSKeyword__moz_mac_focusring, LookAndFeel::eColorID__moz_mac_focusring }, + { eCSSKeyword__moz_mac_menuselect, LookAndFeel::eColorID__moz_mac_menuselect }, + { eCSSKeyword__moz_mac_menushadow, LookAndFeel::eColorID__moz_mac_menushadow }, + { eCSSKeyword__moz_mac_menutextdisable, LookAndFeel::eColorID__moz_mac_menutextdisable }, + { eCSSKeyword__moz_mac_menutextselect, LookAndFeel::eColorID__moz_mac_menutextselect }, + { eCSSKeyword__moz_mac_disabledtoolbartext, LookAndFeel::eColorID__moz_mac_disabledtoolbartext }, + { eCSSKeyword__moz_mac_secondaryhighlight, LookAndFeel::eColorID__moz_mac_secondaryhighlight }, + { eCSSKeyword__moz_menuhover, LookAndFeel::eColorID__moz_menuhover }, + { eCSSKeyword__moz_menuhovertext, LookAndFeel::eColorID__moz_menuhovertext }, + { eCSSKeyword__moz_menubartext, LookAndFeel::eColorID__moz_menubartext }, + { eCSSKeyword__moz_menubarhovertext, LookAndFeel::eColorID__moz_menubarhovertext }, + { eCSSKeyword__moz_oddtreerow, LookAndFeel::eColorID__moz_oddtreerow }, + { eCSSKeyword__moz_visitedhyperlinktext, NS_COLOR_MOZ_VISITEDHYPERLINKTEXT }, + { eCSSKeyword_currentcolor, NS_COLOR_CURRENTCOLOR }, + { eCSSKeyword__moz_win_mediatext, LookAndFeel::eColorID__moz_win_mediatext }, + { eCSSKeyword__moz_win_communicationstext, LookAndFeel::eColorID__moz_win_communicationstext }, + { eCSSKeyword__moz_nativehyperlinktext, LookAndFeel::eColorID__moz_nativehyperlinktext }, + { eCSSKeyword__moz_comboboxtext, LookAndFeel::eColorID__moz_comboboxtext }, + { eCSSKeyword__moz_combobox, LookAndFeel::eColorID__moz_combobox }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kContentKTable[] = { + { eCSSKeyword_open_quote, NS_STYLE_CONTENT_OPEN_QUOTE }, + { eCSSKeyword_close_quote, NS_STYLE_CONTENT_CLOSE_QUOTE }, + { eCSSKeyword_no_open_quote, NS_STYLE_CONTENT_NO_OPEN_QUOTE }, + { eCSSKeyword_no_close_quote, NS_STYLE_CONTENT_NO_CLOSE_QUOTE }, + { eCSSKeyword__moz_alt_content, NS_STYLE_CONTENT_ALT_CONTENT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kControlCharacterVisibilityKTable[] = { + { eCSSKeyword_hidden, NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN }, + { eCSSKeyword_visible, NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCounterRangeKTable[] = { + { eCSSKeyword_infinite, NS_STYLE_COUNTER_RANGE_INFINITE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCounterSpeakAsKTable[] = { + { eCSSKeyword_bullets, NS_STYLE_COUNTER_SPEAKAS_BULLETS }, + { eCSSKeyword_numbers, NS_STYLE_COUNTER_SPEAKAS_NUMBERS }, + { eCSSKeyword_words, NS_STYLE_COUNTER_SPEAKAS_WORDS }, + { eCSSKeyword_spell_out, NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCounterSymbolsSystemKTable[] = { + { eCSSKeyword_cyclic, NS_STYLE_COUNTER_SYSTEM_CYCLIC }, + { eCSSKeyword_numeric, NS_STYLE_COUNTER_SYSTEM_NUMERIC }, + { eCSSKeyword_alphabetic, NS_STYLE_COUNTER_SYSTEM_ALPHABETIC }, + { eCSSKeyword_symbolic, NS_STYLE_COUNTER_SYSTEM_SYMBOLIC }, + { eCSSKeyword_fixed, NS_STYLE_COUNTER_SYSTEM_FIXED }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCounterSystemKTable[] = { + { eCSSKeyword_cyclic, NS_STYLE_COUNTER_SYSTEM_CYCLIC }, + { eCSSKeyword_numeric, NS_STYLE_COUNTER_SYSTEM_NUMERIC }, + { eCSSKeyword_alphabetic, NS_STYLE_COUNTER_SYSTEM_ALPHABETIC }, + { eCSSKeyword_symbolic, NS_STYLE_COUNTER_SYSTEM_SYMBOLIC }, + { eCSSKeyword_additive, NS_STYLE_COUNTER_SYSTEM_ADDITIVE }, + { eCSSKeyword_fixed, NS_STYLE_COUNTER_SYSTEM_FIXED }, + { eCSSKeyword_extends, NS_STYLE_COUNTER_SYSTEM_EXTENDS }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kCursorKTable[] = { + // CSS 2.0 + { eCSSKeyword_auto, NS_STYLE_CURSOR_AUTO }, + { eCSSKeyword_crosshair, NS_STYLE_CURSOR_CROSSHAIR }, + { eCSSKeyword_default, NS_STYLE_CURSOR_DEFAULT }, + { eCSSKeyword_pointer, NS_STYLE_CURSOR_POINTER }, + { eCSSKeyword_move, NS_STYLE_CURSOR_MOVE }, + { eCSSKeyword_e_resize, NS_STYLE_CURSOR_E_RESIZE }, + { eCSSKeyword_ne_resize, NS_STYLE_CURSOR_NE_RESIZE }, + { eCSSKeyword_nw_resize, NS_STYLE_CURSOR_NW_RESIZE }, + { eCSSKeyword_n_resize, NS_STYLE_CURSOR_N_RESIZE }, + { eCSSKeyword_se_resize, NS_STYLE_CURSOR_SE_RESIZE }, + { eCSSKeyword_sw_resize, NS_STYLE_CURSOR_SW_RESIZE }, + { eCSSKeyword_s_resize, NS_STYLE_CURSOR_S_RESIZE }, + { eCSSKeyword_w_resize, NS_STYLE_CURSOR_W_RESIZE }, + { eCSSKeyword_text, NS_STYLE_CURSOR_TEXT }, + { eCSSKeyword_wait, NS_STYLE_CURSOR_WAIT }, + { eCSSKeyword_help, NS_STYLE_CURSOR_HELP }, + // CSS 2.1 + { eCSSKeyword_progress, NS_STYLE_CURSOR_SPINNING }, + // CSS3 basic user interface module + { eCSSKeyword_copy, NS_STYLE_CURSOR_COPY }, + { eCSSKeyword_alias, NS_STYLE_CURSOR_ALIAS }, + { eCSSKeyword_context_menu, NS_STYLE_CURSOR_CONTEXT_MENU }, + { eCSSKeyword_cell, NS_STYLE_CURSOR_CELL }, + { eCSSKeyword_not_allowed, NS_STYLE_CURSOR_NOT_ALLOWED }, + { eCSSKeyword_col_resize, NS_STYLE_CURSOR_COL_RESIZE }, + { eCSSKeyword_row_resize, NS_STYLE_CURSOR_ROW_RESIZE }, + { eCSSKeyword_no_drop, NS_STYLE_CURSOR_NO_DROP }, + { eCSSKeyword_vertical_text, NS_STYLE_CURSOR_VERTICAL_TEXT }, + { eCSSKeyword_all_scroll, NS_STYLE_CURSOR_ALL_SCROLL }, + { eCSSKeyword_nesw_resize, NS_STYLE_CURSOR_NESW_RESIZE }, + { eCSSKeyword_nwse_resize, NS_STYLE_CURSOR_NWSE_RESIZE }, + { eCSSKeyword_ns_resize, NS_STYLE_CURSOR_NS_RESIZE }, + { eCSSKeyword_ew_resize, NS_STYLE_CURSOR_EW_RESIZE }, + { eCSSKeyword_none, NS_STYLE_CURSOR_NONE }, + { eCSSKeyword_grab, NS_STYLE_CURSOR_GRAB }, + { eCSSKeyword_grabbing, NS_STYLE_CURSOR_GRABBING }, + { eCSSKeyword_zoom_in, NS_STYLE_CURSOR_ZOOM_IN }, + { eCSSKeyword_zoom_out, NS_STYLE_CURSOR_ZOOM_OUT }, + // -moz- prefixed vendor specific + { eCSSKeyword__moz_grab, NS_STYLE_CURSOR_GRAB }, + { eCSSKeyword__moz_grabbing, NS_STYLE_CURSOR_GRABBING }, + { eCSSKeyword__moz_zoom_in, NS_STYLE_CURSOR_ZOOM_IN }, + { eCSSKeyword__moz_zoom_out, NS_STYLE_CURSOR_ZOOM_OUT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kDirectionKTable[] = { + { eCSSKeyword_ltr, NS_STYLE_DIRECTION_LTR }, + { eCSSKeyword_rtl, NS_STYLE_DIRECTION_RTL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kDisplayKTable[] = { + { eCSSKeyword_none, StyleDisplay::None }, + { eCSSKeyword_inline, StyleDisplay::Inline }, + { eCSSKeyword_block, StyleDisplay::Block }, + { eCSSKeyword_inline_block, StyleDisplay::InlineBlock }, + { eCSSKeyword_list_item, StyleDisplay::ListItem }, + { eCSSKeyword_table, StyleDisplay::Table }, + { eCSSKeyword_inline_table, StyleDisplay::InlineTable }, + { eCSSKeyword_table_row_group, StyleDisplay::TableRowGroup }, + { eCSSKeyword_table_header_group, StyleDisplay::TableHeaderGroup }, + { eCSSKeyword_table_footer_group, StyleDisplay::TableFooterGroup }, + { eCSSKeyword_table_row, StyleDisplay::TableRow }, + { eCSSKeyword_table_column_group, StyleDisplay::TableColumnGroup }, + { eCSSKeyword_table_column, StyleDisplay::TableColumn }, + { eCSSKeyword_table_cell, StyleDisplay::TableCell }, + { eCSSKeyword_table_caption, StyleDisplay::TableCaption }, + // Make sure this is kept in sync with the code in + // nsCSSFrameConstructor::ConstructXULFrame + { eCSSKeyword__moz_box, StyleDisplay::Box }, + { eCSSKeyword__moz_inline_box, StyleDisplay::InlineBox }, +#ifdef MOZ_XUL + { eCSSKeyword__moz_grid, StyleDisplay::XulGrid }, + { eCSSKeyword__moz_inline_grid, StyleDisplay::InlineXulGrid }, + { eCSSKeyword__moz_grid_group, StyleDisplay::XulGridGroup }, + { eCSSKeyword__moz_grid_line, StyleDisplay::XulGridLine }, + { eCSSKeyword__moz_stack, StyleDisplay::Stack }, + { eCSSKeyword__moz_inline_stack, StyleDisplay::InlineStack }, + { eCSSKeyword__moz_deck, StyleDisplay::Deck }, + { eCSSKeyword__moz_popup, StyleDisplay::Popup }, + { eCSSKeyword__moz_groupbox, StyleDisplay::Groupbox }, +#endif + { eCSSKeyword_flex, StyleDisplay::Flex }, + { eCSSKeyword_inline_flex, StyleDisplay::InlineFlex }, + { eCSSKeyword_ruby, StyleDisplay::Ruby }, + { eCSSKeyword_ruby_base, StyleDisplay::RubyBase }, + { eCSSKeyword_ruby_base_container, StyleDisplay::RubyBaseContainer }, + { eCSSKeyword_ruby_text, StyleDisplay::RubyText }, + { eCSSKeyword_ruby_text_container, StyleDisplay::RubyTextContainer }, + // The next two entries are controlled by the layout.css.grid.enabled pref. + { eCSSKeyword_grid, StyleDisplay::Grid }, + { eCSSKeyword_inline_grid, StyleDisplay::InlineGrid }, + // The next 4 entries are controlled by the layout.css.prefixes.webkit pref. + { eCSSKeyword__webkit_box, StyleDisplay::WebkitBox }, + { eCSSKeyword__webkit_inline_box, StyleDisplay::WebkitInlineBox }, + { eCSSKeyword__webkit_flex, StyleDisplay::Flex }, + { eCSSKeyword__webkit_inline_flex, StyleDisplay::InlineFlex }, + // The next entry is controlled by the layout.css.display-contents.enabled + // pref. + { eCSSKeyword_contents, StyleDisplay::Contents }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kEmptyCellsKTable[] = { + { eCSSKeyword_show, NS_STYLE_TABLE_EMPTY_CELLS_SHOW }, + { eCSSKeyword_hide, NS_STYLE_TABLE_EMPTY_CELLS_HIDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignAllKeywords[] = { + { eCSSKeyword_auto, NS_STYLE_ALIGN_AUTO }, + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + // Also "first/last baseline"; see nsCSSValue::AppendAlignJustifyValueToString + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_self_start, NS_STYLE_ALIGN_SELF_START }, + { eCSSKeyword_self_end, NS_STYLE_ALIGN_SELF_END }, + { eCSSKeyword_space_between, NS_STYLE_ALIGN_SPACE_BETWEEN }, + { eCSSKeyword_space_around, NS_STYLE_ALIGN_SPACE_AROUND }, + { eCSSKeyword_space_evenly, NS_STYLE_ALIGN_SPACE_EVENLY }, + { eCSSKeyword_legacy, NS_STYLE_ALIGN_LEGACY }, + { eCSSKeyword_safe, NS_STYLE_ALIGN_SAFE }, + { eCSSKeyword_unsafe, NS_STYLE_ALIGN_UNSAFE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignOverflowPosition[] = { + { eCSSKeyword_unsafe, NS_STYLE_ALIGN_UNSAFE }, + { eCSSKeyword_safe, NS_STYLE_ALIGN_SAFE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignSelfPosition[] = { + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_self_start, NS_STYLE_ALIGN_SELF_START }, + { eCSSKeyword_self_end, NS_STYLE_ALIGN_SELF_END }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignLegacy[] = { + { eCSSKeyword_legacy, NS_STYLE_ALIGN_LEGACY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignLegacyPosition[] = { + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignAutoNormalStretchBaseline[] = { + { eCSSKeyword_auto, NS_STYLE_ALIGN_AUTO }, + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + // Also "first baseline" & "last baseline"; see CSSParserImpl::ParseAlignEnum + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignNormalStretchBaseline[] = { + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + // Also "first baseline" & "last baseline"; see CSSParserImpl::ParseAlignEnum + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignNormalBaseline[] = { + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + // Also "first baseline" & "last baseline"; see CSSParserImpl::ParseAlignEnum + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignContentDistribution[] = { + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_space_between, NS_STYLE_ALIGN_SPACE_BETWEEN }, + { eCSSKeyword_space_around, NS_STYLE_ALIGN_SPACE_AROUND }, + { eCSSKeyword_space_evenly, NS_STYLE_ALIGN_SPACE_EVENLY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAlignContentPosition[] = { + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// these are only used for auto-completion, not parsing: +const KTableEntry nsCSSProps::kAutoCompletionAlignJustifySelf[] = { + { eCSSKeyword_auto, NS_STYLE_ALIGN_AUTO }, + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + { eCSSKeyword_last_baseline, NS_STYLE_ALIGN_LAST_BASELINE }, + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_self_start, NS_STYLE_ALIGN_SELF_START }, + { eCSSKeyword_self_end, NS_STYLE_ALIGN_SELF_END }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAutoCompletionAlignItems[] = { + // Intentionally no 'auto' here. + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + { eCSSKeyword_last_baseline, NS_STYLE_ALIGN_LAST_BASELINE }, + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_self_start, NS_STYLE_ALIGN_SELF_START }, + { eCSSKeyword_self_end, NS_STYLE_ALIGN_SELF_END }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kAutoCompletionAlignJustifyContent[] = { + // Intentionally no 'auto' here. + { eCSSKeyword_normal, NS_STYLE_ALIGN_NORMAL }, + { eCSSKeyword_baseline, NS_STYLE_ALIGN_BASELINE }, + { eCSSKeyword_last_baseline, NS_STYLE_ALIGN_LAST_BASELINE }, + { eCSSKeyword_stretch, NS_STYLE_ALIGN_STRETCH }, + { eCSSKeyword_space_between, NS_STYLE_ALIGN_SPACE_BETWEEN }, + { eCSSKeyword_space_around, NS_STYLE_ALIGN_SPACE_AROUND }, + { eCSSKeyword_space_evenly, NS_STYLE_ALIGN_SPACE_EVENLY }, + { eCSSKeyword_start, NS_STYLE_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_ALIGN_END }, + { eCSSKeyword_flex_start, NS_STYLE_ALIGN_FLEX_START }, + { eCSSKeyword_flex_end, NS_STYLE_ALIGN_FLEX_END }, + { eCSSKeyword_center, NS_STYLE_ALIGN_CENTER }, + { eCSSKeyword_left, NS_STYLE_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_ALIGN_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; +// + +const KTableEntry nsCSSProps::kFlexDirectionKTable[] = { + { eCSSKeyword_row, NS_STYLE_FLEX_DIRECTION_ROW }, + { eCSSKeyword_row_reverse, NS_STYLE_FLEX_DIRECTION_ROW_REVERSE }, + { eCSSKeyword_column, NS_STYLE_FLEX_DIRECTION_COLUMN }, + { eCSSKeyword_column_reverse, NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFlexWrapKTable[] = { + { eCSSKeyword_nowrap, NS_STYLE_FLEX_WRAP_NOWRAP }, + { eCSSKeyword_wrap, NS_STYLE_FLEX_WRAP_WRAP }, + { eCSSKeyword_wrap_reverse, NS_STYLE_FLEX_WRAP_WRAP_REVERSE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kHyphensKTable[] = { + { eCSSKeyword_none, NS_STYLE_HYPHENS_NONE }, + { eCSSKeyword_manual, NS_STYLE_HYPHENS_MANUAL }, + { eCSSKeyword_auto, NS_STYLE_HYPHENS_AUTO }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kFloatKTable[] = { + { eCSSKeyword_none, StyleFloat::None }, + { eCSSKeyword_left, StyleFloat::Left }, + { eCSSKeyword_right, StyleFloat::Right }, + { eCSSKeyword_inline_start, StyleFloat::InlineStart }, + { eCSSKeyword_inline_end, StyleFloat::InlineEnd }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFloatEdgeKTable[] = { + { eCSSKeyword_content_box, uint8_t(StyleFloatEdge::ContentBox) }, + { eCSSKeyword_margin_box, uint8_t(StyleFloatEdge::MarginBox) }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontDisplayKTable[] = { + { eCSSKeyword_auto, NS_FONT_DISPLAY_AUTO }, + { eCSSKeyword_block, NS_FONT_DISPLAY_BLOCK }, + { eCSSKeyword_swap, NS_FONT_DISPLAY_SWAP }, + { eCSSKeyword_fallback, NS_FONT_DISPLAY_FALLBACK }, + { eCSSKeyword_optional, NS_FONT_DISPLAY_OPTIONAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontKTable[] = { + // CSS2. + { eCSSKeyword_caption, NS_STYLE_FONT_CAPTION }, + { eCSSKeyword_icon, NS_STYLE_FONT_ICON }, + { eCSSKeyword_menu, NS_STYLE_FONT_MENU }, + { eCSSKeyword_message_box, NS_STYLE_FONT_MESSAGE_BOX }, + { eCSSKeyword_small_caption, NS_STYLE_FONT_SMALL_CAPTION }, + { eCSSKeyword_status_bar, NS_STYLE_FONT_STATUS_BAR }, + + // Proposed for CSS3. + { eCSSKeyword__moz_window, NS_STYLE_FONT_WINDOW }, + { eCSSKeyword__moz_document, NS_STYLE_FONT_DOCUMENT }, + { eCSSKeyword__moz_workspace, NS_STYLE_FONT_WORKSPACE }, + { eCSSKeyword__moz_desktop, NS_STYLE_FONT_DESKTOP }, + { eCSSKeyword__moz_info, NS_STYLE_FONT_INFO }, + { eCSSKeyword__moz_dialog, NS_STYLE_FONT_DIALOG }, + { eCSSKeyword__moz_button, NS_STYLE_FONT_BUTTON }, + { eCSSKeyword__moz_pull_down_menu, NS_STYLE_FONT_PULL_DOWN_MENU }, + { eCSSKeyword__moz_list, NS_STYLE_FONT_LIST }, + { eCSSKeyword__moz_field, NS_STYLE_FONT_FIELD }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontKerningKTable[] = { + { eCSSKeyword_auto, NS_FONT_KERNING_AUTO }, + { eCSSKeyword_none, NS_FONT_KERNING_NONE }, + { eCSSKeyword_normal, NS_FONT_KERNING_NORMAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontSizeKTable[] = { + { eCSSKeyword_xx_small, NS_STYLE_FONT_SIZE_XXSMALL }, + { eCSSKeyword_x_small, NS_STYLE_FONT_SIZE_XSMALL }, + { eCSSKeyword_small, NS_STYLE_FONT_SIZE_SMALL }, + { eCSSKeyword_medium, NS_STYLE_FONT_SIZE_MEDIUM }, + { eCSSKeyword_large, NS_STYLE_FONT_SIZE_LARGE }, + { eCSSKeyword_x_large, NS_STYLE_FONT_SIZE_XLARGE }, + { eCSSKeyword_xx_large, NS_STYLE_FONT_SIZE_XXLARGE }, + { eCSSKeyword_larger, NS_STYLE_FONT_SIZE_LARGER }, + { eCSSKeyword_smaller, NS_STYLE_FONT_SIZE_SMALLER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontSmoothingKTable[] = { + { eCSSKeyword_auto, NS_FONT_SMOOTHING_AUTO }, + { eCSSKeyword_grayscale, NS_FONT_SMOOTHING_GRAYSCALE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontStretchKTable[] = { + { eCSSKeyword_ultra_condensed, NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED }, + { eCSSKeyword_extra_condensed, NS_STYLE_FONT_STRETCH_EXTRA_CONDENSED }, + { eCSSKeyword_condensed, NS_STYLE_FONT_STRETCH_CONDENSED }, + { eCSSKeyword_semi_condensed, NS_STYLE_FONT_STRETCH_SEMI_CONDENSED }, + { eCSSKeyword_normal, NS_STYLE_FONT_STRETCH_NORMAL }, + { eCSSKeyword_semi_expanded, NS_STYLE_FONT_STRETCH_SEMI_EXPANDED }, + { eCSSKeyword_expanded, NS_STYLE_FONT_STRETCH_EXPANDED }, + { eCSSKeyword_extra_expanded, NS_STYLE_FONT_STRETCH_EXTRA_EXPANDED }, + { eCSSKeyword_ultra_expanded, NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontStyleKTable[] = { + { eCSSKeyword_normal, NS_STYLE_FONT_STYLE_NORMAL }, + { eCSSKeyword_italic, NS_STYLE_FONT_STYLE_ITALIC }, + { eCSSKeyword_oblique, NS_STYLE_FONT_STYLE_OBLIQUE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontSynthesisKTable[] = { + { eCSSKeyword_weight, NS_FONT_SYNTHESIS_WEIGHT }, + { eCSSKeyword_style, NS_FONT_SYNTHESIS_STYLE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantAlternatesKTable[] = { + { eCSSKeyword_historical_forms, NS_FONT_VARIANT_ALTERNATES_HISTORICAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantAlternatesFuncsKTable[] = { + { eCSSKeyword_stylistic, NS_FONT_VARIANT_ALTERNATES_STYLISTIC }, + { eCSSKeyword_styleset, NS_FONT_VARIANT_ALTERNATES_STYLESET }, + { eCSSKeyword_character_variant, NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT }, + { eCSSKeyword_swash, NS_FONT_VARIANT_ALTERNATES_SWASH }, + { eCSSKeyword_ornaments, NS_FONT_VARIANT_ALTERNATES_ORNAMENTS }, + { eCSSKeyword_annotation, NS_FONT_VARIANT_ALTERNATES_ANNOTATION }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantCapsKTable[] = { + { eCSSKeyword_small_caps, NS_FONT_VARIANT_CAPS_SMALLCAPS }, + { eCSSKeyword_all_small_caps, NS_FONT_VARIANT_CAPS_ALLSMALL }, + { eCSSKeyword_petite_caps, NS_FONT_VARIANT_CAPS_PETITECAPS }, + { eCSSKeyword_all_petite_caps, NS_FONT_VARIANT_CAPS_ALLPETITE }, + { eCSSKeyword_titling_caps, NS_FONT_VARIANT_CAPS_TITLING }, + { eCSSKeyword_unicase, NS_FONT_VARIANT_CAPS_UNICASE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantEastAsianKTable[] = { + { eCSSKeyword_jis78, NS_FONT_VARIANT_EAST_ASIAN_JIS78 }, + { eCSSKeyword_jis83, NS_FONT_VARIANT_EAST_ASIAN_JIS83 }, + { eCSSKeyword_jis90, NS_FONT_VARIANT_EAST_ASIAN_JIS90 }, + { eCSSKeyword_jis04, NS_FONT_VARIANT_EAST_ASIAN_JIS04 }, + { eCSSKeyword_simplified, NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED }, + { eCSSKeyword_traditional, NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL }, + { eCSSKeyword_full_width, NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH }, + { eCSSKeyword_proportional_width, NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH }, + { eCSSKeyword_ruby, NS_FONT_VARIANT_EAST_ASIAN_RUBY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantLigaturesKTable[] = { + { eCSSKeyword_common_ligatures, NS_FONT_VARIANT_LIGATURES_COMMON }, + { eCSSKeyword_no_common_ligatures, NS_FONT_VARIANT_LIGATURES_NO_COMMON }, + { eCSSKeyword_discretionary_ligatures, NS_FONT_VARIANT_LIGATURES_DISCRETIONARY }, + { eCSSKeyword_no_discretionary_ligatures, NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY }, + { eCSSKeyword_historical_ligatures, NS_FONT_VARIANT_LIGATURES_HISTORICAL }, + { eCSSKeyword_no_historical_ligatures, NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL }, + { eCSSKeyword_contextual, NS_FONT_VARIANT_LIGATURES_CONTEXTUAL }, + { eCSSKeyword_no_contextual, NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantNumericKTable[] = { + { eCSSKeyword_lining_nums, NS_FONT_VARIANT_NUMERIC_LINING }, + { eCSSKeyword_oldstyle_nums, NS_FONT_VARIANT_NUMERIC_OLDSTYLE }, + { eCSSKeyword_proportional_nums, NS_FONT_VARIANT_NUMERIC_PROPORTIONAL }, + { eCSSKeyword_tabular_nums, NS_FONT_VARIANT_NUMERIC_TABULAR }, + { eCSSKeyword_diagonal_fractions, NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS }, + { eCSSKeyword_stacked_fractions, NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS }, + { eCSSKeyword_slashed_zero, NS_FONT_VARIANT_NUMERIC_SLASHZERO }, + { eCSSKeyword_ordinal, NS_FONT_VARIANT_NUMERIC_ORDINAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontVariantPositionKTable[] = { + { eCSSKeyword_super, NS_FONT_VARIANT_POSITION_SUPER }, + { eCSSKeyword_sub, NS_FONT_VARIANT_POSITION_SUB }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFontWeightKTable[] = { + { eCSSKeyword_normal, NS_STYLE_FONT_WEIGHT_NORMAL }, + { eCSSKeyword_bold, NS_STYLE_FONT_WEIGHT_BOLD }, + { eCSSKeyword_bolder, NS_STYLE_FONT_WEIGHT_BOLDER }, + { eCSSKeyword_lighter, NS_STYLE_FONT_WEIGHT_LIGHTER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kGridAutoFlowKTable[] = { + { eCSSKeyword_row, NS_STYLE_GRID_AUTO_FLOW_ROW }, + { eCSSKeyword_column, NS_STYLE_GRID_AUTO_FLOW_COLUMN }, + { eCSSKeyword_dense, NS_STYLE_GRID_AUTO_FLOW_DENSE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kGridTrackBreadthKTable[] = { + { eCSSKeyword_min_content, NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT }, + { eCSSKeyword_max_content, NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageOrientationKTable[] = { + { eCSSKeyword_flip, NS_STYLE_IMAGE_ORIENTATION_FLIP }, + { eCSSKeyword_from_image, NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageOrientationFlipKTable[] = { + { eCSSKeyword_flip, NS_STYLE_IMAGE_ORIENTATION_FLIP }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kIsolationKTable[] = { + { eCSSKeyword_auto, NS_STYLE_ISOLATION_AUTO }, + { eCSSKeyword_isolate, NS_STYLE_ISOLATION_ISOLATE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kIMEModeKTable[] = { + { eCSSKeyword_normal, NS_STYLE_IME_MODE_NORMAL }, + { eCSSKeyword_auto, NS_STYLE_IME_MODE_AUTO }, + { eCSSKeyword_active, NS_STYLE_IME_MODE_ACTIVE }, + { eCSSKeyword_disabled, NS_STYLE_IME_MODE_DISABLED }, + { eCSSKeyword_inactive, NS_STYLE_IME_MODE_INACTIVE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kLineHeightKTable[] = { + // -moz- prefixed, intended for internal use for single-line controls + { eCSSKeyword__moz_block_height, NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kListStylePositionKTable[] = { + { eCSSKeyword_inside, NS_STYLE_LIST_STYLE_POSITION_INSIDE }, + { eCSSKeyword_outside, NS_STYLE_LIST_STYLE_POSITION_OUTSIDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kListStyleKTable[] = { + // none and decimal are not redefinable, so they should not be moved. + { eCSSKeyword_none, NS_STYLE_LIST_STYLE_NONE }, + { eCSSKeyword_decimal, NS_STYLE_LIST_STYLE_DECIMAL }, + // the following graphic styles are processed in a different way. + { eCSSKeyword_disc, NS_STYLE_LIST_STYLE_DISC }, + { eCSSKeyword_circle, NS_STYLE_LIST_STYLE_CIRCLE }, + { eCSSKeyword_square, NS_STYLE_LIST_STYLE_SQUARE }, + { eCSSKeyword_disclosure_closed, NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED }, + { eCSSKeyword_disclosure_open, NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN }, + // the following counter styles require specific algorithms to generate. + { eCSSKeyword_hebrew, NS_STYLE_LIST_STYLE_HEBREW }, + { eCSSKeyword_japanese_informal, NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL }, + { eCSSKeyword_japanese_formal, NS_STYLE_LIST_STYLE_JAPANESE_FORMAL }, + { eCSSKeyword_korean_hangul_formal, NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL }, + { eCSSKeyword_korean_hanja_informal, NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL }, + { eCSSKeyword_korean_hanja_formal, NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL }, + { eCSSKeyword_simp_chinese_informal, NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL }, + { eCSSKeyword_simp_chinese_formal, NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL }, + { eCSSKeyword_trad_chinese_informal, NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL }, + { eCSSKeyword_trad_chinese_formal, NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL }, + { eCSSKeyword_ethiopic_numeric, NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kMathVariantKTable[] = { + { eCSSKeyword_none, NS_MATHML_MATHVARIANT_NONE }, + { eCSSKeyword_normal, NS_MATHML_MATHVARIANT_NORMAL }, + { eCSSKeyword_bold, NS_MATHML_MATHVARIANT_BOLD }, + { eCSSKeyword_italic, NS_MATHML_MATHVARIANT_ITALIC }, + { eCSSKeyword_bold_italic, NS_MATHML_MATHVARIANT_BOLD_ITALIC }, + { eCSSKeyword_script, NS_MATHML_MATHVARIANT_SCRIPT }, + { eCSSKeyword_bold_script, NS_MATHML_MATHVARIANT_BOLD_SCRIPT }, + { eCSSKeyword_fraktur, NS_MATHML_MATHVARIANT_FRAKTUR }, + { eCSSKeyword_double_struck, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK }, + { eCSSKeyword_bold_fraktur, NS_MATHML_MATHVARIANT_BOLD_FRAKTUR }, + { eCSSKeyword_sans_serif, NS_MATHML_MATHVARIANT_SANS_SERIF }, + { eCSSKeyword_bold_sans_serif, NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF }, + { eCSSKeyword_sans_serif_italic, NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC }, + { eCSSKeyword_sans_serif_bold_italic, NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC }, + { eCSSKeyword_monospace, NS_MATHML_MATHVARIANT_MONOSPACE }, + { eCSSKeyword_initial, NS_MATHML_MATHVARIANT_INITIAL }, + { eCSSKeyword_tailed, NS_MATHML_MATHVARIANT_TAILED }, + { eCSSKeyword_looped, NS_MATHML_MATHVARIANT_LOOPED }, + { eCSSKeyword_stretched, NS_MATHML_MATHVARIANT_STRETCHED }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kMathDisplayKTable[] = { + { eCSSKeyword_inline, NS_MATHML_DISPLAYSTYLE_INLINE }, + { eCSSKeyword_block, NS_MATHML_DISPLAYSTYLE_BLOCK }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kContainKTable[] = { + { eCSSKeyword_none, NS_STYLE_CONTAIN_NONE }, + { eCSSKeyword_strict, NS_STYLE_CONTAIN_STRICT }, + { eCSSKeyword_layout, NS_STYLE_CONTAIN_LAYOUT }, + { eCSSKeyword_style, NS_STYLE_CONTAIN_STYLE }, + { eCSSKeyword_paint, NS_STYLE_CONTAIN_PAINT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kContextOpacityKTable[] = { + { eCSSKeyword_context_fill_opacity, NS_STYLE_CONTEXT_FILL_OPACITY }, + { eCSSKeyword_context_stroke_opacity, NS_STYLE_CONTEXT_STROKE_OPACITY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kContextPatternKTable[] = { + { eCSSKeyword_context_fill, NS_COLOR_CONTEXT_FILL }, + { eCSSKeyword_context_stroke, NS_COLOR_CONTEXT_STROKE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kObjectFitKTable[] = { + { eCSSKeyword_fill, NS_STYLE_OBJECT_FIT_FILL }, + { eCSSKeyword_contain, NS_STYLE_OBJECT_FIT_CONTAIN }, + { eCSSKeyword_cover, NS_STYLE_OBJECT_FIT_COVER }, + { eCSSKeyword_none, NS_STYLE_OBJECT_FIT_NONE }, + { eCSSKeyword_scale_down, NS_STYLE_OBJECT_FIT_SCALE_DOWN }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kOrientKTable[] = { + { eCSSKeyword_inline, StyleOrient::Inline }, + { eCSSKeyword_block, StyleOrient::Block }, + { eCSSKeyword_horizontal, StyleOrient::Horizontal }, + { eCSSKeyword_vertical, StyleOrient::Vertical }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// Same as kBorderStyleKTable except 'hidden'. +const KTableEntry nsCSSProps::kOutlineStyleKTable[] = { + { eCSSKeyword_none, NS_STYLE_BORDER_STYLE_NONE }, + { eCSSKeyword_auto, NS_STYLE_BORDER_STYLE_AUTO }, + { eCSSKeyword_dotted, NS_STYLE_BORDER_STYLE_DOTTED }, + { eCSSKeyword_dashed, NS_STYLE_BORDER_STYLE_DASHED }, + { eCSSKeyword_solid, NS_STYLE_BORDER_STYLE_SOLID }, + { eCSSKeyword_double, NS_STYLE_BORDER_STYLE_DOUBLE }, + { eCSSKeyword_groove, NS_STYLE_BORDER_STYLE_GROOVE }, + { eCSSKeyword_ridge, NS_STYLE_BORDER_STYLE_RIDGE }, + { eCSSKeyword_inset, NS_STYLE_BORDER_STYLE_INSET }, + { eCSSKeyword_outset, NS_STYLE_BORDER_STYLE_OUTSET }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kOverflowKTable[] = { + { eCSSKeyword_auto, NS_STYLE_OVERFLOW_AUTO }, + { eCSSKeyword_visible, NS_STYLE_OVERFLOW_VISIBLE }, + { eCSSKeyword_hidden, NS_STYLE_OVERFLOW_HIDDEN }, + { eCSSKeyword_scroll, NS_STYLE_OVERFLOW_SCROLL }, + // Deprecated: + { eCSSKeyword__moz_scrollbars_none, NS_STYLE_OVERFLOW_HIDDEN }, + { eCSSKeyword__moz_scrollbars_horizontal, NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL }, + { eCSSKeyword__moz_scrollbars_vertical, NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL }, + { eCSSKeyword__moz_hidden_unscrollable, NS_STYLE_OVERFLOW_CLIP }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kOverflowClipBoxKTable[] = { + { eCSSKeyword_padding_box, NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX }, + { eCSSKeyword_content_box, NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kOverflowSubKTable[] = { + { eCSSKeyword_auto, NS_STYLE_OVERFLOW_AUTO }, + { eCSSKeyword_visible, NS_STYLE_OVERFLOW_VISIBLE }, + { eCSSKeyword_hidden, NS_STYLE_OVERFLOW_HIDDEN }, + { eCSSKeyword_scroll, NS_STYLE_OVERFLOW_SCROLL }, + // Deprecated: + { eCSSKeyword__moz_hidden_unscrollable, NS_STYLE_OVERFLOW_CLIP }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPageBreakKTable[] = { + { eCSSKeyword_auto, NS_STYLE_PAGE_BREAK_AUTO }, + { eCSSKeyword_always, NS_STYLE_PAGE_BREAK_ALWAYS }, + { eCSSKeyword_avoid, NS_STYLE_PAGE_BREAK_AVOID }, + { eCSSKeyword_left, NS_STYLE_PAGE_BREAK_LEFT }, + { eCSSKeyword_right, NS_STYLE_PAGE_BREAK_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPageBreakInsideKTable[] = { + { eCSSKeyword_auto, NS_STYLE_PAGE_BREAK_AUTO }, + { eCSSKeyword_avoid, NS_STYLE_PAGE_BREAK_AVOID }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPageMarksKTable[] = { + { eCSSKeyword_none, NS_STYLE_PAGE_MARKS_NONE }, + { eCSSKeyword_crop, NS_STYLE_PAGE_MARKS_CROP }, + { eCSSKeyword_cross, NS_STYLE_PAGE_MARKS_REGISTER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPageSizeKTable[] = { + { eCSSKeyword_landscape, NS_STYLE_PAGE_SIZE_LANDSCAPE }, + { eCSSKeyword_portrait, NS_STYLE_PAGE_SIZE_PORTRAIT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPointerEventsKTable[] = { + { eCSSKeyword_none, NS_STYLE_POINTER_EVENTS_NONE }, + { eCSSKeyword_visiblepainted, NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED }, + { eCSSKeyword_visiblefill, NS_STYLE_POINTER_EVENTS_VISIBLEFILL }, + { eCSSKeyword_visiblestroke, NS_STYLE_POINTER_EVENTS_VISIBLESTROKE }, + { eCSSKeyword_visible, NS_STYLE_POINTER_EVENTS_VISIBLE }, + { eCSSKeyword_painted, NS_STYLE_POINTER_EVENTS_PAINTED }, + { eCSSKeyword_fill, NS_STYLE_POINTER_EVENTS_FILL }, + { eCSSKeyword_stroke, NS_STYLE_POINTER_EVENTS_STROKE }, + { eCSSKeyword_all, NS_STYLE_POINTER_EVENTS_ALL }, + { eCSSKeyword_auto, NS_STYLE_POINTER_EVENTS_AUTO }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kPositionKTable[] = { + { eCSSKeyword_static, NS_STYLE_POSITION_STATIC }, + { eCSSKeyword_relative, NS_STYLE_POSITION_RELATIVE }, + { eCSSKeyword_absolute, NS_STYLE_POSITION_ABSOLUTE }, + { eCSSKeyword_fixed, NS_STYLE_POSITION_FIXED }, + { eCSSKeyword_sticky, NS_STYLE_POSITION_STICKY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kRadialGradientShapeKTable[] = { + { eCSSKeyword_circle, NS_STYLE_GRADIENT_SHAPE_CIRCULAR }, + { eCSSKeyword_ellipse, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kRadialGradientSizeKTable[] = { + { eCSSKeyword_closest_side, NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE }, + { eCSSKeyword_closest_corner, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER }, + { eCSSKeyword_farthest_side, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE }, + { eCSSKeyword_farthest_corner, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kRadialGradientLegacySizeKTable[] = { + { eCSSKeyword_closest_side, NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE }, + { eCSSKeyword_closest_corner, NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER }, + { eCSSKeyword_farthest_side, NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE }, + { eCSSKeyword_farthest_corner, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER }, + // synonyms + { eCSSKeyword_contain, NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE }, + { eCSSKeyword_cover, NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kResizeKTable[] = { + { eCSSKeyword_none, NS_STYLE_RESIZE_NONE }, + { eCSSKeyword_both, NS_STYLE_RESIZE_BOTH }, + { eCSSKeyword_horizontal, NS_STYLE_RESIZE_HORIZONTAL }, + { eCSSKeyword_vertical, NS_STYLE_RESIZE_VERTICAL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kRubyAlignKTable[] = { + { eCSSKeyword_start, NS_STYLE_RUBY_ALIGN_START }, + { eCSSKeyword_center, NS_STYLE_RUBY_ALIGN_CENTER }, + { eCSSKeyword_space_between, NS_STYLE_RUBY_ALIGN_SPACE_BETWEEN }, + { eCSSKeyword_space_around, NS_STYLE_RUBY_ALIGN_SPACE_AROUND }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kRubyPositionKTable[] = { + { eCSSKeyword_over, NS_STYLE_RUBY_POSITION_OVER }, + { eCSSKeyword_under, NS_STYLE_RUBY_POSITION_UNDER }, + // bug 1055672 for 'inter-character' support + // { eCSSKeyword_inter_character, NS_STYLE_RUBY_POSITION_INTER_CHARACTER }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kScrollBehaviorKTable[] = { + { eCSSKeyword_auto, NS_STYLE_SCROLL_BEHAVIOR_AUTO }, + { eCSSKeyword_smooth, NS_STYLE_SCROLL_BEHAVIOR_SMOOTH }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kScrollSnapTypeKTable[] = { + { eCSSKeyword_none, NS_STYLE_SCROLL_SNAP_TYPE_NONE }, + { eCSSKeyword_mandatory, NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY }, + { eCSSKeyword_proximity, NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kStackSizingKTable[] = { + { eCSSKeyword_ignore, NS_STYLE_STACK_SIZING_IGNORE }, + { eCSSKeyword_stretch_to_fit, NS_STYLE_STACK_SIZING_STRETCH_TO_FIT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTableLayoutKTable[] = { + { eCSSKeyword_auto, NS_STYLE_TABLE_LAYOUT_AUTO }, + { eCSSKeyword_fixed, NS_STYLE_TABLE_LAYOUT_FIXED }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kTextAlignKTable[] = { + { eCSSKeyword_left, NS_STYLE_TEXT_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_TEXT_ALIGN_RIGHT }, + { eCSSKeyword_center, NS_STYLE_TEXT_ALIGN_CENTER }, + { eCSSKeyword_justify, NS_STYLE_TEXT_ALIGN_JUSTIFY }, + { eCSSKeyword__moz_center, NS_STYLE_TEXT_ALIGN_MOZ_CENTER }, + { eCSSKeyword__moz_right, NS_STYLE_TEXT_ALIGN_MOZ_RIGHT }, + { eCSSKeyword__moz_left, NS_STYLE_TEXT_ALIGN_MOZ_LEFT }, + { eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END }, + { eCSSKeyword_unsafe, NS_STYLE_TEXT_ALIGN_UNSAFE }, + { eCSSKeyword_match_parent, NS_STYLE_TEXT_ALIGN_MATCH_PARENT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +KTableEntry nsCSSProps::kTextAlignLastKTable[] = { + { eCSSKeyword_auto, NS_STYLE_TEXT_ALIGN_AUTO }, + { eCSSKeyword_left, NS_STYLE_TEXT_ALIGN_LEFT }, + { eCSSKeyword_right, NS_STYLE_TEXT_ALIGN_RIGHT }, + { eCSSKeyword_center, NS_STYLE_TEXT_ALIGN_CENTER }, + { eCSSKeyword_justify, NS_STYLE_TEXT_ALIGN_JUSTIFY }, + { eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_START }, + { eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END }, + { eCSSKeyword_unsafe, NS_STYLE_TEXT_ALIGN_UNSAFE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextCombineUprightKTable[] = { + { eCSSKeyword_none, NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE }, + { eCSSKeyword_all, NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL }, + { eCSSKeyword_digits, NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2 }, // w/o number ==> 2 + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextDecorationLineKTable[] = { + { eCSSKeyword_none, NS_STYLE_TEXT_DECORATION_LINE_NONE }, + { eCSSKeyword_underline, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE }, + { eCSSKeyword_overline, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE }, + { eCSSKeyword_line_through, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH }, + { eCSSKeyword_blink, NS_STYLE_TEXT_DECORATION_LINE_BLINK }, + { eCSSKeyword__moz_anchor_decoration, NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextDecorationStyleKTable[] = { + { eCSSKeyword__moz_none, NS_STYLE_TEXT_DECORATION_STYLE_NONE }, + { eCSSKeyword_solid, NS_STYLE_TEXT_DECORATION_STYLE_SOLID }, + { eCSSKeyword_double, NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE }, + { eCSSKeyword_dotted, NS_STYLE_TEXT_DECORATION_STYLE_DOTTED }, + { eCSSKeyword_dashed, NS_STYLE_TEXT_DECORATION_STYLE_DASHED }, + { eCSSKeyword_wavy, NS_STYLE_TEXT_DECORATION_STYLE_WAVY }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextOrientationKTable[] = { + { eCSSKeyword_mixed, NS_STYLE_TEXT_ORIENTATION_MIXED }, + { eCSSKeyword_upright, NS_STYLE_TEXT_ORIENTATION_UPRIGHT }, + { eCSSKeyword_sideways, NS_STYLE_TEXT_ORIENTATION_SIDEWAYS }, + { eCSSKeyword_sideways_right, NS_STYLE_TEXT_ORIENTATION_SIDEWAYS }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextEmphasisPositionKTable[] = { + { eCSSKeyword_over, NS_STYLE_TEXT_EMPHASIS_POSITION_OVER }, + { eCSSKeyword_under, NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER }, + { eCSSKeyword_left, NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT }, + { eCSSKeyword_right, NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextEmphasisStyleFillKTable[] = { + { eCSSKeyword_filled, NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED }, + { eCSSKeyword_open, NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextEmphasisStyleShapeKTable[] = { + { eCSSKeyword_dot, NS_STYLE_TEXT_EMPHASIS_STYLE_DOT }, + { eCSSKeyword_circle, NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE }, + { eCSSKeyword_double_circle, NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE }, + { eCSSKeyword_triangle, NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE }, + { eCSSKeyword_sesame, NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME} , + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextOverflowKTable[] = { + { eCSSKeyword_clip, NS_STYLE_TEXT_OVERFLOW_CLIP }, + { eCSSKeyword_ellipsis, NS_STYLE_TEXT_OVERFLOW_ELLIPSIS }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextTransformKTable[] = { + { eCSSKeyword_none, NS_STYLE_TEXT_TRANSFORM_NONE }, + { eCSSKeyword_capitalize, NS_STYLE_TEXT_TRANSFORM_CAPITALIZE }, + { eCSSKeyword_lowercase, NS_STYLE_TEXT_TRANSFORM_LOWERCASE }, + { eCSSKeyword_uppercase, NS_STYLE_TEXT_TRANSFORM_UPPERCASE }, + { eCSSKeyword_full_width, NS_STYLE_TEXT_TRANSFORM_FULL_WIDTH }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTouchActionKTable[] = { + { eCSSKeyword_none, NS_STYLE_TOUCH_ACTION_NONE }, + { eCSSKeyword_auto, NS_STYLE_TOUCH_ACTION_AUTO }, + { eCSSKeyword_pan_x, NS_STYLE_TOUCH_ACTION_PAN_X }, + { eCSSKeyword_pan_y, NS_STYLE_TOUCH_ACTION_PAN_Y }, + { eCSSKeyword_manipulation, NS_STYLE_TOUCH_ACTION_MANIPULATION }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTopLayerKTable[] = { + { eCSSKeyword_none, NS_STYLE_TOP_LAYER_NONE }, + { eCSSKeyword_top, NS_STYLE_TOP_LAYER_TOP }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTransformBoxKTable[] = { + { eCSSKeyword_border_box, NS_STYLE_TRANSFORM_BOX_BORDER_BOX }, + { eCSSKeyword_fill_box, NS_STYLE_TRANSFORM_BOX_FILL_BOX }, + { eCSSKeyword_view_box, NS_STYLE_TRANSFORM_BOX_VIEW_BOX }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTransitionTimingFunctionKTable[] = { + { eCSSKeyword_ease, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE }, + { eCSSKeyword_linear, NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR }, + { eCSSKeyword_ease_in, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN }, + { eCSSKeyword_ease_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT }, + { eCSSKeyword_ease_in_out, NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT }, + { eCSSKeyword_step_start, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START }, + { eCSSKeyword_step_end, NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kUnicodeBidiKTable[] = { + { eCSSKeyword_normal, NS_STYLE_UNICODE_BIDI_NORMAL }, + { eCSSKeyword_embed, NS_STYLE_UNICODE_BIDI_EMBED }, + { eCSSKeyword_bidi_override, NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE }, + { eCSSKeyword_isolate, NS_STYLE_UNICODE_BIDI_ISOLATE }, + { eCSSKeyword_isolate_override, NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE }, + { eCSSKeyword_plaintext, NS_STYLE_UNICODE_BIDI_PLAINTEXT }, + { eCSSKeyword__moz_isolate, NS_STYLE_UNICODE_BIDI_ISOLATE }, + { eCSSKeyword__moz_isolate_override, NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE }, + { eCSSKeyword__moz_plaintext, NS_STYLE_UNICODE_BIDI_PLAINTEXT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kUserFocusKTable[] = { + { eCSSKeyword_none, uint8_t(StyleUserFocus::None) }, + { eCSSKeyword_normal, uint8_t(StyleUserFocus::Normal) }, + { eCSSKeyword_ignore, uint8_t(StyleUserFocus::Ignore) }, + { eCSSKeyword_select_all, uint8_t(StyleUserFocus::SelectAll) }, + { eCSSKeyword_select_before, uint8_t(StyleUserFocus::SelectBefore) }, + { eCSSKeyword_select_after, uint8_t(StyleUserFocus::SelectAfter) }, + { eCSSKeyword_select_same, uint8_t(StyleUserFocus::SelectSame) }, + { eCSSKeyword_select_menu, uint8_t(StyleUserFocus::SelectMenu) }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kUserInputKTable[] = { + { eCSSKeyword_none, StyleUserInput::None }, + { eCSSKeyword_enabled, StyleUserInput::Enabled }, + { eCSSKeyword_disabled, StyleUserInput::Disabled }, + { eCSSKeyword_auto, StyleUserInput::Auto }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kUserModifyKTable[] = { + { eCSSKeyword_read_only, StyleUserModify::ReadOnly }, + { eCSSKeyword_read_write, StyleUserModify::ReadWrite }, + { eCSSKeyword_write_only, StyleUserModify::WriteOnly }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kUserSelectKTable[] = { + { eCSSKeyword_none, StyleUserSelect::None }, + { eCSSKeyword_auto, StyleUserSelect::Auto }, + { eCSSKeyword_text, StyleUserSelect::Text }, + { eCSSKeyword_element, StyleUserSelect::Element }, + { eCSSKeyword_elements, StyleUserSelect::Elements }, + { eCSSKeyword_all, StyleUserSelect::All }, + { eCSSKeyword_toggle, StyleUserSelect::Toggle }, + { eCSSKeyword_tri_state, StyleUserSelect::TriState }, + { eCSSKeyword__moz_all, StyleUserSelect::MozAll }, + { eCSSKeyword__moz_none, StyleUserSelect::None }, + { eCSSKeyword__moz_text, StyleUserSelect::MozText }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kVerticalAlignKTable[] = { + { eCSSKeyword_baseline, NS_STYLE_VERTICAL_ALIGN_BASELINE }, + { eCSSKeyword_sub, NS_STYLE_VERTICAL_ALIGN_SUB }, + { eCSSKeyword_super, NS_STYLE_VERTICAL_ALIGN_SUPER }, + { eCSSKeyword_top, NS_STYLE_VERTICAL_ALIGN_TOP }, + { eCSSKeyword_text_top, NS_STYLE_VERTICAL_ALIGN_TEXT_TOP }, + { eCSSKeyword_middle, NS_STYLE_VERTICAL_ALIGN_MIDDLE }, + { eCSSKeyword__moz_middle_with_baseline, NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE }, + { eCSSKeyword_bottom, NS_STYLE_VERTICAL_ALIGN_BOTTOM }, + { eCSSKeyword_text_bottom, NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kVisibilityKTable[] = { + { eCSSKeyword_visible, NS_STYLE_VISIBILITY_VISIBLE }, + { eCSSKeyword_hidden, NS_STYLE_VISIBILITY_HIDDEN }, + { eCSSKeyword_collapse, NS_STYLE_VISIBILITY_COLLAPSE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWhitespaceKTable[] = { + { eCSSKeyword_normal, NS_STYLE_WHITESPACE_NORMAL }, + { eCSSKeyword_pre, NS_STYLE_WHITESPACE_PRE }, + { eCSSKeyword_nowrap, NS_STYLE_WHITESPACE_NOWRAP }, + { eCSSKeyword_pre_wrap, NS_STYLE_WHITESPACE_PRE_WRAP }, + { eCSSKeyword_pre_line, NS_STYLE_WHITESPACE_PRE_LINE }, + { eCSSKeyword__moz_pre_space, NS_STYLE_WHITESPACE_PRE_SPACE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWidthKTable[] = { + { eCSSKeyword__moz_max_content, NS_STYLE_WIDTH_MAX_CONTENT }, + { eCSSKeyword__moz_min_content, NS_STYLE_WIDTH_MIN_CONTENT }, + { eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT }, + { eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWindowDraggingKTable[] = { + { eCSSKeyword_default, StyleWindowDragging::Default }, + { eCSSKeyword_drag, StyleWindowDragging::Drag }, + { eCSSKeyword_no_drag, StyleWindowDragging::NoDrag }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWindowShadowKTable[] = { + { eCSSKeyword_none, NS_STYLE_WINDOW_SHADOW_NONE }, + { eCSSKeyword_default, NS_STYLE_WINDOW_SHADOW_DEFAULT }, + { eCSSKeyword_menu, NS_STYLE_WINDOW_SHADOW_MENU }, + { eCSSKeyword_tooltip, NS_STYLE_WINDOW_SHADOW_TOOLTIP }, + { eCSSKeyword_sheet, NS_STYLE_WINDOW_SHADOW_SHEET }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWordBreakKTable[] = { + { eCSSKeyword_normal, NS_STYLE_WORDBREAK_NORMAL }, + { eCSSKeyword_break_all, NS_STYLE_WORDBREAK_BREAK_ALL }, + { eCSSKeyword_keep_all, NS_STYLE_WORDBREAK_KEEP_ALL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kOverflowWrapKTable[] = { + { eCSSKeyword_normal, NS_STYLE_OVERFLOWWRAP_NORMAL }, + { eCSSKeyword_break_word, NS_STYLE_OVERFLOWWRAP_BREAK_WORD }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kWritingModeKTable[] = { + { eCSSKeyword_horizontal_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB }, + { eCSSKeyword_vertical_lr, NS_STYLE_WRITING_MODE_VERTICAL_LR }, + { eCSSKeyword_vertical_rl, NS_STYLE_WRITING_MODE_VERTICAL_RL }, + { eCSSKeyword_sideways_lr, NS_STYLE_WRITING_MODE_SIDEWAYS_LR }, + { eCSSKeyword_sideways_rl, NS_STYLE_WRITING_MODE_SIDEWAYS_RL }, + { eCSSKeyword_lr, NS_STYLE_WRITING_MODE_HORIZONTAL_TB }, + { eCSSKeyword_lr_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB }, + { eCSSKeyword_rl, NS_STYLE_WRITING_MODE_HORIZONTAL_TB }, + { eCSSKeyword_rl_tb, NS_STYLE_WRITING_MODE_HORIZONTAL_TB }, + { eCSSKeyword_tb, NS_STYLE_WRITING_MODE_VERTICAL_RL }, + { eCSSKeyword_tb_rl, NS_STYLE_WRITING_MODE_VERTICAL_RL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// Specific keyword tables for XUL.properties +const KTableEntry nsCSSProps::kBoxAlignKTable[] = { + { eCSSKeyword_stretch, StyleBoxAlign::Stretch }, + { eCSSKeyword_start, StyleBoxAlign::Start }, + { eCSSKeyword_center, StyleBoxAlign::Center }, + { eCSSKeyword_baseline, StyleBoxAlign::Baseline }, + { eCSSKeyword_end, StyleBoxAlign::End }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxDirectionKTable[] = { + { eCSSKeyword_normal, StyleBoxDirection::Normal }, + { eCSSKeyword_reverse, StyleBoxDirection::Reverse }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxOrientKTable[] = { + { eCSSKeyword_horizontal, StyleBoxOrient::Horizontal }, + { eCSSKeyword_vertical, StyleBoxOrient::Vertical }, + { eCSSKeyword_inline_axis, StyleBoxOrient::Horizontal }, + { eCSSKeyword_block_axis, StyleBoxOrient::Vertical }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kBoxPackKTable[] = { + { eCSSKeyword_start, StyleBoxPack::Start }, + { eCSSKeyword_center, StyleBoxPack::Center }, + { eCSSKeyword_end, StyleBoxPack::End }, + { eCSSKeyword_justify, StyleBoxPack::Justify }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// keyword tables for SVG properties + +const KTableEntry nsCSSProps::kDominantBaselineKTable[] = { + { eCSSKeyword_auto, NS_STYLE_DOMINANT_BASELINE_AUTO }, + { eCSSKeyword_use_script, NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT }, + { eCSSKeyword_no_change, NS_STYLE_DOMINANT_BASELINE_NO_CHANGE }, + { eCSSKeyword_reset_size, NS_STYLE_DOMINANT_BASELINE_RESET_SIZE }, + { eCSSKeyword_alphabetic, NS_STYLE_DOMINANT_BASELINE_ALPHABETIC }, + { eCSSKeyword_hanging, NS_STYLE_DOMINANT_BASELINE_HANGING }, + { eCSSKeyword_ideographic, NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC }, + { eCSSKeyword_mathematical, NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL }, + { eCSSKeyword_central, NS_STYLE_DOMINANT_BASELINE_CENTRAL }, + { eCSSKeyword_middle, NS_STYLE_DOMINANT_BASELINE_MIDDLE }, + { eCSSKeyword_text_after_edge, NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE }, + { eCSSKeyword_text_before_edge, NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFillRuleKTable[] = { + { eCSSKeyword_nonzero, StyleFillRule::Nonzero }, + { eCSSKeyword_evenodd, StyleFillRule::Evenodd }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kClipPathGeometryBoxKTable[] = { + { eCSSKeyword_content_box, StyleClipPathGeometryBox::Content }, + { eCSSKeyword_padding_box, StyleClipPathGeometryBox::Padding }, + { eCSSKeyword_border_box, StyleClipPathGeometryBox::Border }, + { eCSSKeyword_margin_box, StyleClipPathGeometryBox::Margin }, + { eCSSKeyword_fill_box, StyleClipPathGeometryBox::Fill }, + { eCSSKeyword_stroke_box, StyleClipPathGeometryBox::Stroke }, + { eCSSKeyword_view_box, StyleClipPathGeometryBox::View }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kShapeRadiusKTable[] = { + { eCSSKeyword_closest_side, NS_RADIUS_CLOSEST_SIDE }, + { eCSSKeyword_farthest_side, NS_RADIUS_FARTHEST_SIDE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kFilterFunctionKTable[] = { + { eCSSKeyword_blur, NS_STYLE_FILTER_BLUR }, + { eCSSKeyword_brightness, NS_STYLE_FILTER_BRIGHTNESS }, + { eCSSKeyword_contrast, NS_STYLE_FILTER_CONTRAST }, + { eCSSKeyword_grayscale, NS_STYLE_FILTER_GRAYSCALE }, + { eCSSKeyword_invert, NS_STYLE_FILTER_INVERT }, + { eCSSKeyword_opacity, NS_STYLE_FILTER_OPACITY }, + { eCSSKeyword_saturate, NS_STYLE_FILTER_SATURATE }, + { eCSSKeyword_sepia, NS_STYLE_FILTER_SEPIA }, + { eCSSKeyword_hue_rotate, NS_STYLE_FILTER_HUE_ROTATE }, + { eCSSKeyword_drop_shadow, NS_STYLE_FILTER_DROP_SHADOW }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kImageRenderingKTable[] = { + { eCSSKeyword_auto, NS_STYLE_IMAGE_RENDERING_AUTO }, + { eCSSKeyword_optimizespeed, NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED }, + { eCSSKeyword_optimizequality, NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY }, + { eCSSKeyword__moz_crisp_edges, NS_STYLE_IMAGE_RENDERING_CRISPEDGES }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kMaskTypeKTable[] = { + { eCSSKeyword_luminance, NS_STYLE_MASK_TYPE_LUMINANCE }, + { eCSSKeyword_alpha, NS_STYLE_MASK_TYPE_ALPHA }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kShapeOutsideShapeBoxKTable[] = { + { eCSSKeyword_content_box, StyleShapeOutsideShapeBox::Content }, + { eCSSKeyword_padding_box, StyleShapeOutsideShapeBox::Padding }, + { eCSSKeyword_border_box, StyleShapeOutsideShapeBox::Border }, + { eCSSKeyword_margin_box, StyleShapeOutsideShapeBox::Margin }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kShapeRenderingKTable[] = { + { eCSSKeyword_auto, NS_STYLE_SHAPE_RENDERING_AUTO }, + { eCSSKeyword_optimizespeed, NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED }, + { eCSSKeyword_crispedges, NS_STYLE_SHAPE_RENDERING_CRISPEDGES }, + { eCSSKeyword_geometricprecision, NS_STYLE_SHAPE_RENDERING_GEOMETRICPRECISION }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kStrokeLinecapKTable[] = { + { eCSSKeyword_butt, NS_STYLE_STROKE_LINECAP_BUTT }, + { eCSSKeyword_round, NS_STYLE_STROKE_LINECAP_ROUND }, + { eCSSKeyword_square, NS_STYLE_STROKE_LINECAP_SQUARE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kStrokeLinejoinKTable[] = { + { eCSSKeyword_miter, NS_STYLE_STROKE_LINEJOIN_MITER }, + { eCSSKeyword_round, NS_STYLE_STROKE_LINEJOIN_ROUND }, + { eCSSKeyword_bevel, NS_STYLE_STROKE_LINEJOIN_BEVEL }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +// Lookup table to store the sole objectValue keyword to let SVG glyphs inherit +// certain stroke-* properties from the outer text object +const KTableEntry nsCSSProps::kStrokeContextValueKTable[] = { + { eCSSKeyword_context_value, NS_STYLE_STROKE_PROP_CONTEXT_VALUE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextAnchorKTable[] = { + { eCSSKeyword_start, NS_STYLE_TEXT_ANCHOR_START }, + { eCSSKeyword_middle, NS_STYLE_TEXT_ANCHOR_MIDDLE }, + { eCSSKeyword_end, NS_STYLE_TEXT_ANCHOR_END }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kTextRenderingKTable[] = { + { eCSSKeyword_auto, NS_STYLE_TEXT_RENDERING_AUTO }, + { eCSSKeyword_optimizespeed, NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED }, + { eCSSKeyword_optimizelegibility, NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY }, + { eCSSKeyword_geometricprecision, NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kVectorEffectKTable[] = { + { eCSSKeyword_none, NS_STYLE_VECTOR_EFFECT_NONE }, + { eCSSKeyword_non_scaling_stroke, NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kColorAdjustKTable[] = { + { eCSSKeyword_economy, NS_STYLE_COLOR_ADJUST_ECONOMY }, + { eCSSKeyword_exact, NS_STYLE_COLOR_ADJUST_EXACT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kColorInterpolationKTable[] = { + { eCSSKeyword_auto, NS_STYLE_COLOR_INTERPOLATION_AUTO }, + { eCSSKeyword_srgb, NS_STYLE_COLOR_INTERPOLATION_SRGB }, + { eCSSKeyword_linearrgb, NS_STYLE_COLOR_INTERPOLATION_LINEARRGB }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +const KTableEntry nsCSSProps::kColumnFillKTable[] = { + { eCSSKeyword_auto, NS_STYLE_COLUMN_FILL_AUTO }, + { eCSSKeyword_balance, NS_STYLE_COLUMN_FILL_BALANCE }, + { eCSSKeyword_UNKNOWN, -1 } +}; + +static inline bool +IsKeyValSentinel(const KTableEntry& aTableEntry) +{ + return aTableEntry.mKeyword == eCSSKeyword_UNKNOWN && + aTableEntry.mValue == -1; +} + +int32_t +nsCSSProps::FindIndexOfKeyword(nsCSSKeyword aKeyword, + const KTableEntry aTable[]) +{ + if (eCSSKeyword_UNKNOWN == aKeyword) { + // NOTE: we can have keyword tables where eCSSKeyword_UNKNOWN is used + // not only for the sentinel, but also in the middle of the table to + // knock out values that have been disabled by prefs, e.g. kDisplayKTable. + // So we deal with eCSSKeyword_UNKNOWN up front to avoid returning a valid + // index in the loop below. + return -1; + } + for (int32_t i = 0; ; ++i) { + const KTableEntry& entry = aTable[i]; + if (::IsKeyValSentinel(entry)) { + break; + } + if (aKeyword == entry.mKeyword) { + return i; + } + } + return -1; +} + +bool +nsCSSProps::FindKeyword(nsCSSKeyword aKeyword, const KTableEntry aTable[], + int32_t& aResult) +{ + int32_t index = FindIndexOfKeyword(aKeyword, aTable); + if (index >= 0) { + aResult = aTable[index].mValue; + return true; + } + return false; +} + +nsCSSKeyword +nsCSSProps::ValueToKeywordEnum(int32_t aValue, const KTableEntry aTable[]) +{ +#ifdef DEBUG + typedef decltype(aTable[0].mValue) table_value_type; + NS_ASSERTION(table_value_type(aValue) == aValue, "Value out of range"); +#endif + for (int32_t i = 0; ; ++i) { + const KTableEntry& entry = aTable[i]; + if (::IsKeyValSentinel(entry)) { + break; + } + if (aValue == entry.mValue) { + return entry.mKeyword; + } + } + return eCSSKeyword_UNKNOWN; +} + +const nsAFlatCString& +nsCSSProps::ValueToKeyword(int32_t aValue, const KTableEntry aTable[]) +{ + nsCSSKeyword keyword = ValueToKeywordEnum(aValue, aTable); + if (keyword == eCSSKeyword_UNKNOWN) { + static nsDependentCString sNullStr(""); + return sNullStr; + } else { + return nsCSSKeywords::GetStringValue(keyword); + } +} + +/* static */ const KTableEntry* const +nsCSSProps::kKeywordTableTable[eCSSProperty_COUNT_no_shorthands] = { + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + kwtable_, + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP +}; + +const nsAFlatCString& +nsCSSProps::LookupPropertyValue(nsCSSPropertyID aProp, int32_t aValue) +{ + MOZ_ASSERT(aProp >= 0 && aProp < eCSSProperty_COUNT, + "property out of range"); +#ifdef DEBUG + typedef decltype(KTableEntry::mValue) table_value_type; + NS_ASSERTION(table_value_type(aValue) == aValue, "Value out of range"); +#endif + + const KTableEntry* kwtable = nullptr; + if (aProp < eCSSProperty_COUNT_no_shorthands) + kwtable = kKeywordTableTable[aProp]; + + if (kwtable) + return ValueToKeyword(aValue, kwtable); + + static nsDependentCString sNullStr(""); + return sNullStr; +} + +bool nsCSSProps::GetColorName(int32_t aPropValue, nsCString &aStr) +{ + bool rv = false; + + // first get the keyword corresponding to the property Value from the color table + nsCSSKeyword keyword = ValueToKeywordEnum(aPropValue, kColorKTable); + + // next get the name as a string from the keywords table + if (keyword != eCSSKeyword_UNKNOWN) { + nsCSSKeywords::AddRefTable(); + aStr = nsCSSKeywords::GetStringValue(keyword); + nsCSSKeywords::ReleaseTable(); + rv = true; + } + return rv; +} + +const nsStyleStructID nsCSSProps::kSIDTable[eCSSProperty_COUNT_no_shorthands] = { + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + eStyleStruct_##stylestruct_, + #define CSS_PROP_LIST_INCLUDE_LOGICAL + + #include "nsCSSPropList.h" + + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP +}; + +const nsStyleAnimType +nsCSSProps::kAnimTypeTable[eCSSProperty_COUNT_no_shorthands] = { +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + animtype_, +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +}; + +const ptrdiff_t +nsCSSProps::kStyleStructOffsetTable[eCSSProperty_COUNT_no_shorthands] = { +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + stylestructoffset_, +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +}; + +const uint32_t nsCSSProps::kFlagsTable[eCSSProperty_COUNT] = { +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + flags_, +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) flags_, +#include "nsCSSPropList.h" +#undef CSS_PROP_SHORTHAND +}; + +static const nsCSSPropertyID gAllSubpropTable[] = { +#define CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \ + stylestruct_, stylestructoffset_, animtype_) \ + eCSSProperty_##id_, +#include "nsCSSPropList.h" +#undef CSS_PROP +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gAnimationSubpropTable[] = { + eCSSProperty_animation_duration, + eCSSProperty_animation_timing_function, + eCSSProperty_animation_delay, + eCSSProperty_animation_direction, + eCSSProperty_animation_fill_mode, + eCSSProperty_animation_iteration_count, + eCSSProperty_animation_play_state, + // List animation-name last so we serialize it last, in case it has + // a value that conflicts with one of the other properties. (See + // how Declaration::GetValue serializes 'animation'. + eCSSProperty_animation_name, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderRadiusSubpropTable[] = { + // Code relies on these being in topleft-topright-bottomright-bottomleft + // order. + eCSSProperty_border_top_left_radius, + eCSSProperty_border_top_right_radius, + eCSSProperty_border_bottom_right_radius, + eCSSProperty_border_bottom_left_radius, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gOutlineRadiusSubpropTable[] = { + // Code relies on these being in topleft-topright-bottomright-bottomleft + // order. + eCSSProperty__moz_outline_radius_topLeft, + eCSSProperty__moz_outline_radius_topRight, + eCSSProperty__moz_outline_radius_bottomRight, + eCSSProperty__moz_outline_radius_bottomLeft, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBackgroundSubpropTable[] = { + eCSSProperty_background_color, + eCSSProperty_background_image, + eCSSProperty_background_repeat, + eCSSProperty_background_attachment, + eCSSProperty_background_clip, + eCSSProperty_background_origin, + eCSSProperty_background_position_x, + eCSSProperty_background_position_y, + eCSSProperty_background_size, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBackgroundPositionSubpropTable[] = { + eCSSProperty_background_position_x, + eCSSProperty_background_position_y, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderSubpropTable[] = { + eCSSProperty_border_top_width, + eCSSProperty_border_right_width, + eCSSProperty_border_bottom_width, + eCSSProperty_border_left_width, + eCSSProperty_border_top_style, + eCSSProperty_border_right_style, + eCSSProperty_border_bottom_style, + eCSSProperty_border_left_style, + eCSSProperty_border_top_color, + eCSSProperty_border_right_color, + eCSSProperty_border_bottom_color, + eCSSProperty_border_left_color, + eCSSProperty_border_top_colors, + eCSSProperty_border_right_colors, + eCSSProperty_border_bottom_colors, + eCSSProperty_border_left_colors, + eCSSProperty_border_image_source, + eCSSProperty_border_image_slice, + eCSSProperty_border_image_width, + eCSSProperty_border_image_outset, + eCSSProperty_border_image_repeat, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderBlockEndSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_block_end_width, + eCSSProperty_border_block_end_style, + eCSSProperty_border_block_end_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderBlockStartSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_block_start_width, + eCSSProperty_border_block_start_style, + eCSSProperty_border_block_start_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderBottomSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_bottom_width, + eCSSProperty_border_bottom_style, + eCSSProperty_border_bottom_color, + eCSSProperty_UNKNOWN +}; + +static_assert(NS_SIDE_TOP == 0 && NS_SIDE_RIGHT == 1 && + NS_SIDE_BOTTOM == 2 && NS_SIDE_LEFT == 3, + "box side constants not top/right/bottom/left == 0/1/2/3"); +static const nsCSSPropertyID gBorderColorSubpropTable[] = { + // Code relies on these being in top-right-bottom-left order. + // Code relies on these matching the NS_SIDE_* constants. + eCSSProperty_border_top_color, + eCSSProperty_border_right_color, + eCSSProperty_border_bottom_color, + eCSSProperty_border_left_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderInlineEndSubpropTable[] = { + // Declaration.cpp output the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_inline_end_width, + eCSSProperty_border_inline_end_style, + eCSSProperty_border_inline_end_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderLeftSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_left_width, + eCSSProperty_border_left_style, + eCSSProperty_border_left_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderRightSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_right_width, + eCSSProperty_border_right_style, + eCSSProperty_border_right_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderInlineStartSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_inline_start_width, + eCSSProperty_border_inline_start_style, + eCSSProperty_border_inline_start_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderStyleSubpropTable[] = { + // Code relies on these being in top-right-bottom-left order. + eCSSProperty_border_top_style, + eCSSProperty_border_right_style, + eCSSProperty_border_bottom_style, + eCSSProperty_border_left_style, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderTopSubpropTable[] = { + // Declaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_border_top_width, + eCSSProperty_border_top_style, + eCSSProperty_border_top_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderWidthSubpropTable[] = { + // Code relies on these being in top-right-bottom-left order. + eCSSProperty_border_top_width, + eCSSProperty_border_right_width, + eCSSProperty_border_bottom_width, + eCSSProperty_border_left_width, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gFontSubpropTable[] = { + eCSSProperty_font_family, + eCSSProperty_font_style, + eCSSProperty_font_weight, + eCSSProperty_font_size, + eCSSProperty_line_height, + eCSSProperty_font_size_adjust, + eCSSProperty_font_stretch, + eCSSProperty__x_system_font, + eCSSProperty_font_feature_settings, + eCSSProperty_font_language_override, + eCSSProperty_font_kerning, + eCSSProperty_font_synthesis, + eCSSProperty_font_variant_alternates, + eCSSProperty_font_variant_caps, + eCSSProperty_font_variant_east_asian, + eCSSProperty_font_variant_ligatures, + eCSSProperty_font_variant_numeric, + eCSSProperty_font_variant_position, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gFontVariantSubpropTable[] = { + eCSSProperty_font_variant_alternates, + eCSSProperty_font_variant_caps, + eCSSProperty_font_variant_east_asian, + eCSSProperty_font_variant_ligatures, + eCSSProperty_font_variant_numeric, + eCSSProperty_font_variant_position, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gListStyleSubpropTable[] = { + eCSSProperty_list_style_type, + eCSSProperty_list_style_image, + eCSSProperty_list_style_position, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gMarginSubpropTable[] = { + // Code relies on these being in top-right-bottom-left order. + eCSSProperty_margin_top, + eCSSProperty_margin_right, + eCSSProperty_margin_bottom, + eCSSProperty_margin_left, + eCSSProperty_UNKNOWN +}; + + +static const nsCSSPropertyID gOutlineSubpropTable[] = { + // nsCSSDeclaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_outline_width, + eCSSProperty_outline_style, + eCSSProperty_outline_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gColumnsSubpropTable[] = { + eCSSProperty_column_count, + eCSSProperty_column_width, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gColumnRuleSubpropTable[] = { + // nsCSSDeclaration.cpp outputs the subproperties in this order. + // It also depends on the color being third. + eCSSProperty_column_rule_width, + eCSSProperty_column_rule_style, + eCSSProperty_column_rule_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gFlexSubpropTable[] = { + eCSSProperty_flex_grow, + eCSSProperty_flex_shrink, + eCSSProperty_flex_basis, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gFlexFlowSubpropTable[] = { + eCSSProperty_flex_direction, + eCSSProperty_flex_wrap, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridTemplateSubpropTable[] = { + eCSSProperty_grid_template_areas, + eCSSProperty_grid_template_rows, + eCSSProperty_grid_template_columns, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridSubpropTable[] = { + eCSSProperty_grid_template_areas, + eCSSProperty_grid_template_rows, + eCSSProperty_grid_template_columns, + eCSSProperty_grid_auto_flow, + eCSSProperty_grid_auto_rows, + eCSSProperty_grid_auto_columns, + eCSSProperty_grid_row_gap, // can only be reset, not get/set + eCSSProperty_grid_column_gap, // can only be reset, not get/set + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridColumnSubpropTable[] = { + eCSSProperty_grid_column_start, + eCSSProperty_grid_column_end, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridRowSubpropTable[] = { + eCSSProperty_grid_row_start, + eCSSProperty_grid_row_end, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridAreaSubpropTable[] = { + eCSSProperty_grid_row_start, + eCSSProperty_grid_column_start, + eCSSProperty_grid_row_end, + eCSSProperty_grid_column_end, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gGridGapSubpropTable[] = { + eCSSProperty_grid_row_gap, + eCSSProperty_grid_column_gap, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gOverflowSubpropTable[] = { + eCSSProperty_overflow_x, + eCSSProperty_overflow_y, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gPaddingSubpropTable[] = { + // Code relies on these being in top-right-bottom-left order. + eCSSProperty_padding_top, + eCSSProperty_padding_right, + eCSSProperty_padding_bottom, + eCSSProperty_padding_left, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gTextDecorationSubpropTable[] = { + eCSSProperty_text_decoration_color, + eCSSProperty_text_decoration_line, + eCSSProperty_text_decoration_style, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gTextEmphasisSubpropTable[] = { + eCSSProperty_text_emphasis_style, + eCSSProperty_text_emphasis_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gWebkitTextStrokeSubpropTable[] = { + eCSSProperty__webkit_text_stroke_width, + eCSSProperty__webkit_text_stroke_color, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gTransitionSubpropTable[] = { + eCSSProperty_transition_property, + eCSSProperty_transition_duration, + eCSSProperty_transition_timing_function, + eCSSProperty_transition_delay, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gBorderImageSubpropTable[] = { + eCSSProperty_border_image_source, + eCSSProperty_border_image_slice, + eCSSProperty_border_image_width, + eCSSProperty_border_image_outset, + eCSSProperty_border_image_repeat, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gMarkerSubpropTable[] = { + eCSSProperty_marker_start, + eCSSProperty_marker_mid, + eCSSProperty_marker_end, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gPlaceContentSubpropTable[] = { + eCSSProperty_align_content, + eCSSProperty_justify_content, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gPlaceItemsSubpropTable[] = { + eCSSProperty_align_items, + eCSSProperty_justify_items, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gPlaceSelfSubpropTable[] = { + eCSSProperty_align_self, + eCSSProperty_justify_self, + eCSSProperty_UNKNOWN +}; + +// Subproperty tables for shorthands that are just aliases with +// different parsing rules. +static const nsCSSPropertyID gMozTransformSubpropTable[] = { + eCSSProperty_transform, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gScrollSnapTypeSubpropTable[] = { + eCSSProperty_scroll_snap_type_x, + eCSSProperty_scroll_snap_type_y, + eCSSProperty_UNKNOWN +}; +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND +static const nsCSSPropertyID gMaskSubpropTable[] = { + eCSSProperty_mask_image, + eCSSProperty_mask_repeat, + eCSSProperty_mask_position_x, + eCSSProperty_mask_position_y, + eCSSProperty_mask_clip, + eCSSProperty_mask_origin, + eCSSProperty_mask_size, + eCSSProperty_mask_composite, + eCSSProperty_mask_mode, + eCSSProperty_UNKNOWN +}; +static const nsCSSPropertyID gMaskPositionSubpropTable[] = { + eCSSProperty_mask_position_x, + eCSSProperty_mask_position_y, + eCSSProperty_UNKNOWN +}; +#endif +// FIXME: mask-border tables should be added when we implement +// mask-border properties. + +const nsCSSPropertyID *const +nsCSSProps::kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands] = { +#define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_ +// Need an extra level of macro nesting to force expansion of method_ +// params before they get pasted. +#define NSCSSPROPS_INNER_MACRO(method_) g##method_##SubpropTable, +#define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \ + NSCSSPROPS_INNER_MACRO(method_) +#include "nsCSSPropList.h" +#undef CSS_PROP_SHORTHAND +#undef NSCSSPROPS_INNER_MACRO +#undef CSS_PROP_PUBLIC_OR_PRIVATE +}; + + +static const nsCSSPropertyID gOffsetLogicalGroupTable[] = { + eCSSProperty_top, + eCSSProperty_right, + eCSSProperty_bottom, + eCSSProperty_left, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gMaxSizeLogicalGroupTable[] = { + eCSSProperty_max_height, + eCSSProperty_max_width, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gMinSizeLogicalGroupTable[] = { + eCSSProperty_min_height, + eCSSProperty_min_width, + eCSSProperty_UNKNOWN +}; + +static const nsCSSPropertyID gSizeLogicalGroupTable[] = { + eCSSProperty_height, + eCSSProperty_width, + eCSSProperty_UNKNOWN +}; + +const nsCSSPropertyID* const +nsCSSProps::kLogicalGroupTable[eCSSPropertyLogicalGroup_COUNT] = { +#define CSS_PROP_LOGICAL_GROUP_SHORTHAND(id_) g##id_##SubpropTable, +#define CSS_PROP_LOGICAL_GROUP_AXIS(name_) g##name_##LogicalGroupTable, +#define CSS_PROP_LOGICAL_GROUP_BOX(name_) g##name_##LogicalGroupTable, +#include "nsCSSPropLogicalGroupList.h" +#undef CSS_PROP_LOGICAL_GROUP_BOX +#undef CSS_PROP_LOGICAL_GROUP_AXIS +#undef CSS_PROP_LOGICAL_GROUP_SHORTHAND +}; + +// Mapping of logical longhand properties to their logical group (which +// represents the physical longhands the logical properties an correspond +// to). The format is pairs of values, where the first is the logical +// longhand property (an nsCSSPropertyID) and the second is the logical group +// (an nsCSSPropertyLogicalGroup), stored in a flat array (like KTableEntry +// arrays). +static const int gLogicalGroupMappingTable[] = { +#define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, group_, stylestruct_, \ + stylestructoffset_, animtype_) \ + eCSSProperty_##id_, eCSSPropertyLogicalGroup_##group_, +#include "nsCSSPropList.h" +#undef CSS_PROP_LOGICAL +}; + +/* static */ const nsCSSPropertyID* +nsCSSProps::LogicalGroup(nsCSSPropertyID aProperty) +{ + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_LOGICAL), + "aProperty must be a logical longhand property"); + + for (size_t i = 0; i < ArrayLength(gLogicalGroupMappingTable); i += 2) { + if (gLogicalGroupMappingTable[i] == aProperty) { + return kLogicalGroupTable[gLogicalGroupMappingTable[i + 1]]; + } + } + + MOZ_ASSERT(false, "missing gLogicalGroupMappingTable entry"); + return nullptr; +} + + +#define ENUM_DATA_FOR_PROPERTY(name_, id_, method_, flags_, pref_, \ + parsevariant_, kwtable_, stylestructoffset_, \ + animtype_) \ + ePropertyIndex_for_##id_, + +// The order of these enums must match the g*Flags arrays in nsRuleNode.cpp. + +enum FontCheckCounter { + #define CSS_PROP_FONT ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_FONT + ePropertyCount_for_Font +}; + +enum DisplayCheckCounter { + #define CSS_PROP_DISPLAY ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_DISPLAY + ePropertyCount_for_Display +}; + +enum VisibilityCheckCounter { + #define CSS_PROP_VISIBILITY ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_VISIBILITY + ePropertyCount_for_Visibility +}; + +enum MarginCheckCounter { + #define CSS_PROP_MARGIN ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_MARGIN + ePropertyCount_for_Margin +}; + +enum BorderCheckCounter { + #define CSS_PROP_BORDER ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_BORDER + ePropertyCount_for_Border +}; + +enum PaddingCheckCounter { + #define CSS_PROP_PADDING ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_PADDING + ePropertyCount_for_Padding +}; + +enum OutlineCheckCounter { + #define CSS_PROP_OUTLINE ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_OUTLINE + ePropertyCount_for_Outline +}; + +enum ListCheckCounter { + #define CSS_PROP_LIST ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST + ePropertyCount_for_List +}; + +enum ColorCheckCounter { + #define CSS_PROP_COLOR ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_COLOR + ePropertyCount_for_Color +}; + +enum BackgroundCheckCounter { + #define CSS_PROP_BACKGROUND ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_BACKGROUND + ePropertyCount_for_Background +}; + +enum PositionCheckCounter { + #define CSS_PROP_POSITION ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_POSITION + ePropertyCount_for_Position +}; + +enum TableCheckCounter { + #define CSS_PROP_TABLE ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_TABLE + ePropertyCount_for_Table +}; + +enum TableBorderCheckCounter { + #define CSS_PROP_TABLEBORDER ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_TABLEBORDER + ePropertyCount_for_TableBorder +}; + +enum ContentCheckCounter { + #define CSS_PROP_CONTENT ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_CONTENT + ePropertyCount_for_Content +}; + +enum TextCheckCounter { + #define CSS_PROP_TEXT ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_TEXT + ePropertyCount_for_Text +}; + +enum TextResetCheckCounter { + #define CSS_PROP_TEXTRESET ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_TEXTRESET + ePropertyCount_for_TextReset +}; + +enum UserInterfaceCheckCounter { + #define CSS_PROP_USERINTERFACE ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_USERINTERFACE + ePropertyCount_for_UserInterface +}; + +enum UIResetCheckCounter { + #define CSS_PROP_UIRESET ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_UIRESET + ePropertyCount_for_UIReset +}; + +enum XULCheckCounter { + #define CSS_PROP_XUL ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_XUL + ePropertyCount_for_XUL +}; + +enum SVGCheckCounter { + #define CSS_PROP_SVG ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_SVG + ePropertyCount_for_SVG +}; + +enum SVGResetCheckCounter { + #define CSS_PROP_SVGRESET ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_SVGRESET + ePropertyCount_for_SVGReset +}; + +enum ColumnCheckCounter { + #define CSS_PROP_COLUMN ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_COLUMN + ePropertyCount_for_Column +}; + +enum VariablesCheckCounter { + #define CSS_PROP_VARIABLES ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_VARIABLES + ePropertyCount_for_Variables +}; + +enum EffectsCheckCounter { + #define CSS_PROP_EFFECTS ENUM_DATA_FOR_PROPERTY + #include "nsCSSPropList.h" + #undef CSS_PROP_EFFECTS + ePropertyCount_for_Effects +}; + +#undef ENUM_DATA_FOR_PROPERTY + +/* static */ const size_t +nsCSSProps::gPropertyCountInStruct[nsStyleStructID_Length] = { + #define STYLE_STRUCT(name, checkdata_cb) \ + ePropertyCount_for_##name, + #include "nsStyleStructList.h" + #undef STYLE_STRUCT +}; + +/* static */ const size_t +nsCSSProps::gPropertyIndexInStruct[eCSSProperty_COUNT_no_shorthands] = { + + #define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, group_, stylestruct_, \ + stylestructoffset_, animtype_) \ + size_t(-1), + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + ePropertyIndex_for_##id_, + #include "nsCSSPropList.h" + #undef CSS_PROP + #undef CSS_PROP_LOGICAL + +}; + +/* 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_) & CSS_PROPERTY_ENABLED_MASK)) + + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + IS_ENABLED_BY_DEFAULT(flags_), + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #include "nsCSSPropList.h" + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP + + #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \ + IS_ENABLED_BY_DEFAULT(flags_), + #include "nsCSSPropList.h" + #undef CSS_PROP_SHORTHAND + + #define CSS_PROP_ALIAS(aliasname_, propid_, aliasmethod_, pref_) \ + true, + #include "nsCSSPropAliasList.h" + #undef CSS_PROP_ALIAS + + #undef IS_ENABLED_BY_DEFAULT +}; + +#include "../../dom/base/PropertyUseCounterMap.inc" + +/* static */ const UseCounter +nsCSSProps::gPropertyUseCounter[eCSSProperty_COUNT_no_shorthands] = { + #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_ + #define CSS_PROP_LIST_INCLUDE_LOGICAL + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + static_cast(USE_COUNTER_FOR_CSS_PROPERTY_##method_), + #include "nsCSSPropList.h" + #undef CSS_PROP + #undef CSS_PROP_LIST_INCLUDE_LOGICAL + #undef CSS_PROP_PUBLIC_OR_PRIVATE +}; + +// Check that all logical property flags are used appropriately. +#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, stylestruct_, stylestructoffset_, animtype_) \ + static_assert(!((flags_) & CSS_PROPERTY_LOGICAL), \ + "only properties defined with CSS_PROP_LOGICAL can use " \ + "the CSS_PROPERTY_LOGICAL flag"); \ + static_assert(!((flags_) & CSS_PROPERTY_LOGICAL_AXIS), \ + "only properties defined with CSS_PROP_LOGICAL can use " \ + "the CSS_PROPERTY_LOGICAL_AXIS flag"); \ + static_assert(!((flags_) & CSS_PROPERTY_LOGICAL_BLOCK_AXIS), \ + "only properties defined with CSS_PROP_LOGICAL can use " \ + "the CSS_PROPERTY_LOGICAL_BLOCK_AXIS flag"); \ + static_assert(!((flags_) & CSS_PROPERTY_LOGICAL_END_EDGE), \ + "only properties defined with CSS_PROP_LOGICAL can use " \ + "the CSS_PROPERTY_LOGICAL_END_EDGE flag"); +#define CSS_PROP_LOGICAL(name_, id_, method_, flags_, pref_, parsevariant_, \ + kwtable_, group_, stylestruct_, \ + stylestructoffset_, animtype_) \ + static_assert((flags_) & CSS_PROPERTY_LOGICAL, \ + "properties defined with CSS_PROP_LOGICAL must also use " \ + "the CSS_PROPERTY_LOGICAL flag"); \ + static_assert(!((flags_) & CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED), \ + "CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED has no effect " \ + "on logical properties"); \ + static_assert(!(((flags_) & CSS_PROPERTY_LOGICAL_AXIS) && \ + ((flags_) & CSS_PROPERTY_LOGICAL_END_EDGE)), \ + "CSS_PROPERTY_LOGICAL_END_EDGE makes no sense when used " \ + "with CSS_PROPERTY_LOGICAL_AXIS"); +#include "nsCSSPropList.h" +#undef CSS_PROP_LOGICAL +#undef CSS_PROP + +#include "nsCSSPropsGenerated.inc" diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h new file mode 100644 index 000000000..ab78e6174 --- /dev/null +++ b/layout/style/nsCSSProps.h @@ -0,0 +1,895 @@ +/* -*- 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/. */ + +/* + * methods for dealing with CSS properties and tables of the keyword + * values they accept + */ + +#ifndef nsCSSProps_h___ +#define nsCSSProps_h___ + +#include +#include +#include "nsIAtom.h" +#include "nsString.h" +#include "nsCSSPropertyID.h" +#include "nsStyleStructFwd.h" +#include "nsCSSKeywords.h" +#include "mozilla/CSSEnabledState.h" +#include "mozilla/UseCounter.h" +#include "mozilla/EnumTypeTraits.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 + +// Flags for ParseVariant method +#define VARIANT_KEYWORD 0x000001 // K +#define VARIANT_LENGTH 0x000002 // L +#define VARIANT_PERCENT 0x000004 // P +#define VARIANT_COLOR 0x000008 // C eCSSUnit_*Color, eCSSUnit_Ident (e.g. "red") +#define VARIANT_URL 0x000010 // U +#define VARIANT_NUMBER 0x000020 // N +#define VARIANT_INTEGER 0x000040 // I +#define VARIANT_ANGLE 0x000080 // G +#define VARIANT_FREQUENCY 0x000100 // F +#define VARIANT_TIME 0x000200 // T +#define VARIANT_STRING 0x000400 // S +#define VARIANT_COUNTER 0x000800 // +#define VARIANT_ATTR 0x001000 // +#define VARIANT_IDENTIFIER 0x002000 // D +#define VARIANT_IDENTIFIER_NO_INHERIT 0x004000 // like above, but excluding +// 'inherit' and 'initial' +#define VARIANT_AUTO 0x010000 // A +#define VARIANT_INHERIT 0x020000 // H eCSSUnit_Initial, eCSSUnit_Inherit, eCSSUnit_Unset +#define VARIANT_NONE 0x040000 // O +#define VARIANT_NORMAL 0x080000 // M +#define VARIANT_SYSFONT 0x100000 // eCSSUnit_System_Font +#define VARIANT_GRADIENT 0x200000 // eCSSUnit_Gradient +#define VARIANT_TIMING_FUNCTION 0x400000 // cubic-bezier() and steps() +#define VARIANT_ALL 0x800000 // +#define VARIANT_IMAGE_RECT 0x01000000 // eCSSUnit_Function +// This is an extra bit that says that a VARIANT_ANGLE allows unitless zero: +#define VARIANT_ZERO_ANGLE 0x02000000 // unitless zero for angles +#define VARIANT_CALC 0x04000000 // eCSSUnit_Calc +#define VARIANT_ELEMENT 0x08000000 // eCSSUnit_Element +#define VARIANT_NONNEGATIVE_DIMENSION 0x10000000 // Only lengths greater than or equal to 0.0 +// Keyword used iff gfx.font_rendering.opentype_svg.enabled is true: +#define VARIANT_OPENTYPE_SVG_KEYWORD 0x20000000 +#define VARIANT_ABSOLUTE_DIMENSION 0x40000000 // B Only lengths with absolute length unit + +// Variants that can consume more than one token +#define VARIANT_MULTIPLE_TOKENS \ + (VARIANT_COLOR | /* rgb(...), hsl(...), etc. */ \ + VARIANT_COUNTER | /* counter(...), counters(...) */ \ + VARIANT_ATTR | /* attr(...) */ \ + VARIANT_GRADIENT | /* linear-gradient(...), etc. */ \ + VARIANT_TIMING_FUNCTION | /* cubic-bezier(...), steps(...) */ \ + VARIANT_IMAGE_RECT | /* -moz-image-rect(...) */ \ + VARIANT_CALC | /* calc(...) */ \ + VARIANT_ELEMENT) /* -moz-element(...) */ + +// Common combinations of variants +#define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH) +#define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT) +#define VARIANT_LN (VARIANT_LENGTH | VARIANT_NUMBER) +#define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT) +#define VARIANT_AHLP (VARIANT_AH | VARIANT_LP) +#define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER) +#define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD) +#define VARIANT_AHKLP (VARIANT_AHLP | VARIANT_KEYWORD) +#define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH) +#define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH) +#define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD) +#define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY) +#define VARIANT_HKI (VARIANT_HK | VARIANT_INTEGER) +#define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH) +#define VARIANT_HKLP (VARIANT_HK | VARIANT_LP) +#define VARIANT_HKLPO (VARIANT_HKLP | VARIANT_NONE) +#define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH) +#define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER) +#define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT) +#define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER) +#define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE) +#define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT) +#define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL) +#define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR) +#define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR) +#define VARIANT_HUK (VARIANT_HK | VARIANT_URL) +#define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE) +#define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO) +#define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER) +#define VARIANT_PN (VARIANT_PERCENT | VARIANT_NUMBER) +#define VARIANT_ALPN (VARIANT_AL | VARIANT_PN) +#define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER) +#define VARIANT_HON (VARIANT_HN | VARIANT_NONE) +#define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING) +#define VARIANT_LPN (VARIANT_LP | VARIANT_NUMBER) +#define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD) +#define VARIANT_UO (VARIANT_URL | VARIANT_NONE) +#define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE) +#define VARIANT_LB (VARIANT_LENGTH | VARIANT_ABSOLUTE_DIMENSION) +#define VARIANT_LBCALC (VARIANT_LB | VARIANT_CALC) +#define VARIANT_LCALC (VARIANT_LENGTH | VARIANT_CALC) +#define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT) +#define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER) +#define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT) +#define VARIANT_IMAGE (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \ + VARIANT_IMAGE_RECT | VARIANT_ELEMENT) + +// Flags for the kFlagsTable bitfield (flags_ in nsCSSPropList.h) + +// This property is a logical property (such as padding-inline-start). +#define CSS_PROPERTY_LOGICAL (1<<0) + +#define CSS_PROPERTY_VALUE_LIST_USES_COMMAS (1<<1) /* otherwise spaces */ + +#define CSS_PROPERTY_APPLIES_TO_FIRST_LETTER (1<<2) +#define CSS_PROPERTY_APPLIES_TO_FIRST_LINE (1<<3) +#define CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE \ + (CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | CSS_PROPERTY_APPLIES_TO_FIRST_LINE) + +// Note that 'background-color' is ignored differently from the other +// properties that have this set, but that's just special-cased. +#define CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED (1<<4) + +// A property that needs to have image loads started when a URL value +// for the property is used for an element. This is supported only +// for a few possible value formats: image directly in the value; list +// of images; and with CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0, image in slot +// 0 of an array, or list of such arrays. +#define CSS_PROPERTY_START_IMAGE_LOADS (1<<5) + +// Should be set only for properties with START_IMAGE_LOADS. Indicates +// that the property has an array value with a URL/image value at index +// 0 in the array, rather than the URL/image being in the value or value +// list. +#define CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0 (1<<6) + +// This is a logical property that represents some value associated with +// a logical axis rather than a logical box side, and thus has two +// corresponding physical properties it could set rather than four. For +// example, the block-size logical property has this flag set, as it +// represents the size in either the block or inline axis dimensions, and +// has two corresponding physical properties, width and height. Must not +// be used in conjunction with CSS_PROPERTY_LOGICAL_END_EDGE. +#define CSS_PROPERTY_LOGICAL_AXIS (1<<7) + +// This property allows calc() between lengths and percentages and +// stores such calc() expressions in its style structs (typically in an +// nsStyleCoord, although this is not the case for 'background-position' +// and 'background-size'). +#define CSS_PROPERTY_STORES_CALC (1<<8) + +// Define what mechanism the CSS parser uses for parsing the property. +// See CSSParserImpl::ParseProperty(nsCSSPropertyID). Don't use 0 so that +// we can verify that every property sets one of the values. +// +// CSS_PROPERTY_PARSE_FUNCTION must be used for shorthand properties, +// since it's the only mechanism that allows appending values for +// separate properties. Longhand properties that require custom parsing +// functions should prefer using CSS_PROPERTY_PARSE_VALUE (or +// CSS_PROPERTY_PARSE_VALUE_LIST) and +// CSS_PROPERTY_VALUE_PARSER_FUNCTION, though a number of existing +// longhand properties use CSS_PROPERTY_PARSE_FUNCTION instead. +#define CSS_PROPERTY_PARSE_PROPERTY_MASK (7<<9) +#define CSS_PROPERTY_PARSE_INACCESSIBLE (1<<9) +#define CSS_PROPERTY_PARSE_FUNCTION (2<<9) +#define CSS_PROPERTY_PARSE_VALUE (3<<9) +#define CSS_PROPERTY_PARSE_VALUE_LIST (4<<9) + +// See CSSParserImpl::ParseSingleValueProperty and comment above +// CSS_PROPERTY_PARSE_FUNCTION (which is different). +#define CSS_PROPERTY_VALUE_PARSER_FUNCTION (1<<12) +static_assert((CSS_PROPERTY_PARSE_PROPERTY_MASK & + CSS_PROPERTY_VALUE_PARSER_FUNCTION) == 0, + "didn't leave enough room for the parse property constants"); + +#define CSS_PROPERTY_VALUE_RESTRICTION_MASK (3<<13) +// The parser (in particular, CSSParserImpl::ParseSingleValueProperty) +// should enforce that the value of this property must be 0 or larger. +#define CSS_PROPERTY_VALUE_NONNEGATIVE (1<<13) +// The parser (in particular, CSSParserImpl::ParseSingleValueProperty) +// should enforce that the value of this property must be 1 or larger. +#define CSS_PROPERTY_VALUE_AT_LEAST_ONE (2<<13) + +// Does this property support the hashless hex color quirk in quirks mode? +#define CSS_PROPERTY_HASHLESS_COLOR_QUIRK (1<<15) + +// Does this property support the unitless length quirk in quirks mode? +#define CSS_PROPERTY_UNITLESS_LENGTH_QUIRK (1<<16) + +// Is this property (which must be a shorthand) really an alias? +#define CSS_PROPERTY_IS_ALIAS (1<<17) + +// Does the property apply to ::placeholder? +#define CSS_PROPERTY_APPLIES_TO_PLACEHOLDER (1<<18) + +// This property is allowed in an @page rule. +#define CSS_PROPERTY_APPLIES_TO_PAGE_RULE (1<<19) + +// This property's getComputedStyle implementation requires layout to be +// flushed. +#define CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH (1<<20) + +// This property requires a stacking context. +#define CSS_PROPERTY_CREATES_STACKING_CONTEXT (1<<21) + +// 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. +#define CSS_PROPERTY_ENABLED_MASK (3<<22) +#define CSS_PROPERTY_ENABLED_IN_UA_SHEETS (1<<22) +#define CSS_PROPERTY_ENABLED_IN_CHROME (1<<23) +#define CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME \ + (CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_ENABLED_IN_CHROME) + +// This property's unitless values are pixels. +#define CSS_PROPERTY_NUMBERS_ARE_PIXELS (1<<24) + +// This property is a logical property for one of the two block axis +// sides (such as margin-block-start or margin-block-end). Must only be +// set if CSS_PROPERTY_LOGICAL is set. When not set, the logical +// property is for one of the two inline axis sides (such as +// margin-inline-start or margin-inline-end). +#define CSS_PROPERTY_LOGICAL_BLOCK_AXIS (1<<25) + +// This property is a logical property for the "end" edge of the +// axis determined by the presence or absence of +// CSS_PROPERTY_LOGICAL_BLOCK_AXIS (such as margin-block-end or +// margin-inline-end). Must only be set if CSS_PROPERTY_LOGICAL is set. +// When not set, the logical property is for the "start" edge (such as +// margin-block-start or margin-inline-start). +#define CSS_PROPERTY_LOGICAL_END_EDGE (1<<26) + +// This property can be animated on the compositor. +#define CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR (1<<27) + +// This property is an internal property that is not represented +// in the DOM. Properties with this flag must be defined in an #ifndef +// CSS_PROP_LIST_EXCLUDE_INTERNAL section of nsCSSPropList.h. +#define CSS_PROPERTY_INTERNAL (1<<28) + +// This property has values that can establish a containing block for +// fixed positioned and absolutely positioned elements. +// This should be set for any properties that can cause an element to be +// such a containing block, as implemented in +// nsStyleDisplay::IsFixedPosContainingBlock. +#define CSS_PROPERTY_FIXPOS_CB (1<<29) + +// This property has values that can establish a containing block for +// absolutely positioned elements. +// This should be set for any properties that can cause an element to be +// such a containing block, as implemented in +// nsStyleDisplay::IsAbsPosContainingBlock. +// It does not need to be set for properties that also have +// CSS_PROPERTY_FIXPOS_CB set. +#define CSS_PROPERTY_ABSPOS_CB (1<<30) + +/** + * Types of animatable values. + */ +enum nsStyleAnimType { + // requires a custom implementation in + // StyleAnimationValue::ExtractComputedValue + eStyleAnimType_Custom, + + // nsStyleCoord with animatable values + eStyleAnimType_Coord, + + // same as Coord, except for one side of an nsStyleSides + // listed in the same order as the NS_STYLE_* constants + eStyleAnimType_Sides_Top, + eStyleAnimType_Sides_Right, + eStyleAnimType_Sides_Bottom, + eStyleAnimType_Sides_Left, + + // similar, but for the *pair* of coord members of an nsStyleCorners + // for the relevant corner + eStyleAnimType_Corner_TopLeft, + eStyleAnimType_Corner_TopRight, + eStyleAnimType_Corner_BottomRight, + eStyleAnimType_Corner_BottomLeft, + + // nscoord values + eStyleAnimType_nscoord, + + // float values + eStyleAnimType_float, + + // nscolor values + eStyleAnimType_Color, + + // StyleComplexColor values + eStyleAnimType_ComplexColor, + + // nsStyleSVGPaint values + eStyleAnimType_PaintServer, + + // RefPtr values + eStyleAnimType_Shadow, + + // discrete values + eStyleAnimType_Discrete, + + // property not animatable + eStyleAnimType_None +}; + +// Empty class derived from nsIAtom so that function signatures can +// require an atom from the atom list. +class nsICSSProperty : public nsIAtom {}; + +class nsCSSProps { +public: + typedef mozilla::CSSEnabledState EnabledState; + + struct KTableEntry + { + // KTableEntry objects can be initialized either with an int16_t value + // or a value of an enumeration type that can fit within an int16_t. + + constexpr KTableEntry(nsCSSKeyword aKeyword, int16_t aValue) + : mKeyword(aKeyword) + , mValue(aValue) + { + } + + template::value>::type> + constexpr KTableEntry(nsCSSKeyword aKeyword, T aValue) + : mKeyword(aKeyword) + , mValue(static_cast(aValue)) + { + static_assert(mozilla::EnumTypeFitsWithin::value, + "aValue must be an enum that fits within mValue"); + } + + nsCSSKeyword mKeyword; + int16_t mValue; + }; + + static void AddRefTable(void); + static void ReleaseTable(void); + + // 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. + static nsCSSPropertyID LookupProperty(const nsAString& aProperty, + EnabledState aEnabled); + static nsCSSPropertyID LookupProperty(const nsACString& aProperty, + EnabledState aEnabled); + // As above, but looked up using a property's IDL name. + // eCSSPropertyExtra_variable won't be returned from these methods. + static nsCSSPropertyID LookupPropertyByIDLName( + const nsAString& aPropertyIDLName, + EnabledState aEnabled); + 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 nsAString& aProperty); + static bool IsCustomPropertyName(const nsACString& aProperty); + + static inline bool IsShorthand(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return (aProperty >= eCSSProperty_COUNT_no_shorthands); + } + + // Must be given a longhand property. + static bool IsInherited(nsCSSPropertyID aProperty); + + // Same but for @font-face descriptors + static nsCSSFontDesc LookupFontDesc(const nsAString& aProperty); + static nsCSSFontDesc LookupFontDesc(const nsACString& aProperty); + + // For @counter-style descriptors + static nsCSSCounterDesc LookupCounterDesc(const nsAString& aProperty); + static nsCSSCounterDesc LookupCounterDesc(const nsACString& aProperty); + + // For predefined counter styles which need to be lower-cased during parse + static bool IsPredefinedCounterStyle(const nsAString& aStyle); + static bool IsPredefinedCounterStyle(const nsACString& aStyle); + + // Given a property enum, get the string value + static const nsAFlatCString& GetStringValue(nsCSSPropertyID aProperty); + static const nsAFlatCString& GetStringValue(nsCSSFontDesc aFontDesc); + static const nsAFlatCString& GetStringValue(nsCSSCounterDesc aCounterDesc); + + // Given a CSS Property and a Property Enum Value + // Return back a const nsString& representation of the + // value. Return back nullstr if no value is found + static const nsAFlatCString& LookupPropertyValue(nsCSSPropertyID aProperty, int32_t aValue); + + // Get a color name for a predefined color value like buttonhighlight or activeborder + // Sets the aStr param to the name of the propertyID + static bool GetColorName(int32_t aPropID, nsCString &aStr); + + // Returns the index of |aKeyword| in |aTable|, if it exists there; + // otherwise, returns -1. + // NOTE: Generally, clients should call FindKeyword() instead of this method. + static int32_t FindIndexOfKeyword(nsCSSKeyword aKeyword, + const KTableEntry aTable[]); + + // Find |aKeyword| in |aTable|, if found set |aValue| to its corresponding value. + // If not found, return false and do not set |aValue|. + static bool FindKeyword(nsCSSKeyword aKeyword, const KTableEntry aTable[], + int32_t& aValue); + // Return the first keyword in |aTable| that has the corresponding value |aValue|. + // Return |eCSSKeyword_UNKNOWN| if not found. + static nsCSSKeyword ValueToKeywordEnum(int32_t aValue, + const KTableEntry aTable[]); + template::value>::type> + static nsCSSKeyword ValueToKeywordEnum(T aValue, + const KTableEntry aTable[]) + { + static_assert(mozilla::EnumTypeFitsWithin::value, + "aValue must be an enum that fits within KTableEntry::mValue"); + return ValueToKeywordEnum(static_cast(aValue), aTable); + } + // Ditto but as a string, return "" when not found. + static const nsAFlatCString& ValueToKeyword(int32_t aValue, + const KTableEntry aTable[]); + template::value>::type> + static const nsAFlatCString& ValueToKeyword(T aValue, + const KTableEntry aTable[]) + { + static_assert(mozilla::EnumTypeFitsWithin::value, + "aValue must be an enum that fits within KTableEntry::mValue"); + return ValueToKeyword(static_cast(aValue), aTable); + } + + static const nsStyleStructID kSIDTable[eCSSProperty_COUNT_no_shorthands]; + static const KTableEntry* const kKeywordTableTable[eCSSProperty_COUNT_no_shorthands]; + static const nsStyleAnimType kAnimTypeTable[eCSSProperty_COUNT_no_shorthands]; + static const ptrdiff_t + kStyleStructOffsetTable[eCSSProperty_COUNT_no_shorthands]; + +private: + static const uint32_t kFlagsTable[eCSSProperty_COUNT]; + +public: + static inline bool PropHasFlags(nsCSSPropertyID aProperty, uint32_t aFlags) + { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + MOZ_ASSERT(!(aFlags & CSS_PROPERTY_PARSE_PROPERTY_MASK), + "The CSS_PROPERTY_PARSE_* values are not bitflags; don't pass " + "them to PropHasFlags. You probably want PropertyParseType " + "instead."); + return (nsCSSProps::kFlagsTable[aProperty] & aFlags) == aFlags; + } + + static inline uint32_t PropertyParseType(nsCSSPropertyID aProperty) + { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return nsCSSProps::kFlagsTable[aProperty] & + CSS_PROPERTY_PARSE_PROPERTY_MASK; + } + + static inline uint32_t ValueRestrictions(nsCSSPropertyID aProperty) + { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT, + "out of range"); + return nsCSSProps::kFlagsTable[aProperty] & + CSS_PROPERTY_VALUE_RESTRICTION_MASK; + } + +private: + // Lives in nsCSSParser.cpp for the macros it depends on. + static const uint32_t kParserVariantTable[eCSSProperty_COUNT_no_shorthands]; + +public: + static inline uint32_t ParserVariant(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + return nsCSSProps::kParserVariantTable[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: + static inline + const nsCSSPropertyID * SubpropertyEntryFor(nsCSSPropertyID aProperty) { + MOZ_ASSERT(eCSSProperty_COUNT_no_shorthands <= aProperty && + aProperty < eCSSProperty_COUNT, + "out of range"); + return nsCSSProps::kSubpropertyTable[aProperty - + eCSSProperty_COUNT_no_shorthands]; + } + + // Returns an eCSSProperty_UNKNOWN-terminated array of the shorthand + // properties containing |aProperty|, sorted from those that contain + // the most properties to those that contain the least. + static const nsCSSPropertyID * ShorthandsContaining(nsCSSPropertyID aProperty) { + MOZ_ASSERT(gShorthandsContainingPool, "uninitialized"); + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + return gShorthandsContainingTable[aProperty]; + } +private: + // gShorthandsContainingTable is an array of the return values for + // ShorthandsContaining (arrays of nsCSSPropertyID terminated by + // eCSSProperty_UNKNOWN) pointing into memory in + // gShorthandsContainingPool (which contains all of those arrays in a + // single allocation, and is the one pointer that should be |free|d). + static nsCSSPropertyID *gShorthandsContainingTable[eCSSProperty_COUNT_no_shorthands]; + static nsCSSPropertyID* gShorthandsContainingPool; + static bool BuildShorthandsContainingTable(); + +private: + static const size_t gPropertyCountInStruct[nsStyleStructID_Length]; + static const size_t gPropertyIndexInStruct[eCSSProperty_COUNT_no_shorthands]; +public: + /** + * Return the number of properties that must be cascaded when + * nsRuleNode builds the nsStyle* for aSID. + */ + static size_t PropertyCountInStruct(nsStyleStructID aSID) { + MOZ_ASSERT(0 <= aSID && aSID < nsStyleStructID_Length, + "out of range"); + return gPropertyCountInStruct[aSID]; + } + /** + * Return an index for aProperty that is unique within its SID and in + * the range 0 <= index < PropertyCountInStruct(aSID). + */ + static size_t PropertyIndexInStruct(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + return gPropertyIndexInStruct[aProperty]; + } + +private: + // A table for logical property groups. Indexes are + // nsCSSPropertyLogicalGroup values. + static const nsCSSPropertyID* const + kLogicalGroupTable[eCSSPropertyLogicalGroup_COUNT]; + +public: + /** + * Returns an array of longhand physical properties which can be set by + * the argument, which must be a logical longhand property. The returned + * array is terminated by an eCSSProperty_UNKNOWN value. For example, + * given eCSSProperty_margin_block_start, returns an array of the four + * properties eCSSProperty_margin_top, eCSSProperty_margin_right, + * eCSSProperty_margin_bottom and eCSSProperty_margin_left, followed + * by the sentinel. + * + * When called with a property that has the CSS_PROPERTY_LOGICAL_AXIS + * flag, the returned array will have two values preceding the sentinel; + * otherwise it will have four. + * + * (Note that the running time of this function is proportional to the + * number of logical longhand properties that exist. If we start + * getting too many of these properties, we should make kLogicalGroupTable + * be a simple array of eCSSProperty_COUNT length.) + */ + static const nsCSSPropertyID* LogicalGroup(nsCSSPropertyID aProperty); + +private: + static bool gPropertyEnabled[eCSSProperty_COUNT_with_aliases]; + +private: + // Defined in the generated nsCSSPropsGenerated.inc. + static const char* const kIDLNameTable[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]; + } + +private: + static const int32_t kIDLNameSortPositionTable[eCSSProperty_COUNT]; + +public: + /** + * 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) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases, + "out of range"); + return gPropertyEnabled[aProperty]; + } + + // A table for the use counter associated with each CSS property. If a + // property does not have a use counter defined in UseCounters.conf, then + // its associated entry is |eUseCounter_UNKNOWN|. + static const mozilla::UseCounter gPropertyUseCounter[eCSSProperty_COUNT_no_shorthands]; + +public: + + static mozilla::UseCounter UseCounterFor(nsCSSPropertyID aProperty) { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands, + "out of range"); + return gPropertyUseCounter[aProperty]; + } + + static bool IsEnabled(nsCSSPropertyID aProperty, EnabledState aEnabled) + { + if (IsEnabled(aProperty)) { + return true; + } + if (aEnabled == EnabledState::eIgnoreEnabledState) { + return true; + } + if ((aEnabled & EnabledState::eInUASheets) && + PropHasFlags(aProperty, CSS_PROPERTY_ENABLED_IN_UA_SHEETS)) + { + return true; + } + if ((aEnabled & EnabledState::eInChrome) && + PropHasFlags(aProperty, CSS_PROPERTY_ENABLED_IN_CHROME)) + { + return true; + } + return false; + } + +public: + static void AddRefAtoms(); + static nsICSSProperty* AtomForProperty(nsCSSPropertyID aProperty) + { + MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT); + return gPropertyAtomTable[aProperty]; + } + +#define CSS_PROP(name_, id_, ...) static nsICSSProperty* id_; +#define CSS_PROP_SHORTHAND(name_, id_, ...) CSS_PROP(name_, id_, ...) +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP_SHORTHAND +#undef CSS_PROP + +private: + static nsICSSProperty* gPropertyAtomTable[eCSSProperty_COUNT]; + +public: + +// 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_)) + + // Keyword/Enum value tables + static const KTableEntry kAnimationDirectionKTable[]; + static const KTableEntry kAnimationFillModeKTable[]; + static const KTableEntry kAnimationIterationCountKTable[]; + static const KTableEntry kAnimationPlayStateKTable[]; + static const KTableEntry kAnimationTimingFunctionKTable[]; + static const KTableEntry kAppearanceKTable[]; + static const KTableEntry kAzimuthKTable[]; + static const KTableEntry kBackfaceVisibilityKTable[]; + static const KTableEntry kTransformStyleKTable[]; + static const KTableEntry kImageLayerAttachmentKTable[]; + static const KTableEntry kImageLayerOriginKTable[]; + static const KTableEntry kImageLayerPositionKTable[]; + static const KTableEntry kImageLayerRepeatKTable[]; + static const KTableEntry kImageLayerRepeatPartKTable[]; + static const KTableEntry kImageLayerSizeKTable[]; + static const KTableEntry kImageLayerCompositeKTable[]; + static const KTableEntry kImageLayerModeKTable[]; + // Not const because we modify its entries when the pref + // "layout.css.background-clip.text" changes: + static KTableEntry kBackgroundClipKTable[]; + static const KTableEntry kBlendModeKTable[]; + static const KTableEntry kBorderCollapseKTable[]; + static const KTableEntry kBorderImageRepeatKTable[]; + static const KTableEntry kBorderImageSliceKTable[]; + static const KTableEntry kBorderStyleKTable[]; + static const KTableEntry kBorderWidthKTable[]; + static const KTableEntry kBoxAlignKTable[]; + static const KTableEntry kBoxDecorationBreakKTable[]; + static const KTableEntry kBoxDirectionKTable[]; + static const KTableEntry kBoxOrientKTable[]; + static const KTableEntry kBoxPackKTable[]; + static const KTableEntry kClipPathGeometryBoxKTable[]; + static const KTableEntry kCounterRangeKTable[]; + static const KTableEntry kCounterSpeakAsKTable[]; + static const KTableEntry kCounterSymbolsSystemKTable[]; + static const KTableEntry kCounterSystemKTable[]; + static const KTableEntry kDominantBaselineKTable[]; + static const KTableEntry kShapeRadiusKTable[]; + static const KTableEntry kFillRuleKTable[]; + static const KTableEntry kFilterFunctionKTable[]; + static const KTableEntry kImageRenderingKTable[]; + static const KTableEntry kShapeOutsideShapeBoxKTable[]; + static const KTableEntry kShapeRenderingKTable[]; + static const KTableEntry kStrokeLinecapKTable[]; + static const KTableEntry kStrokeLinejoinKTable[]; + static const KTableEntry kStrokeContextValueKTable[]; + static const KTableEntry kVectorEffectKTable[]; + static const KTableEntry kTextAnchorKTable[]; + static const KTableEntry kTextRenderingKTable[]; + static const KTableEntry kColorAdjustKTable[]; + static const KTableEntry kColorInterpolationKTable[]; + static const KTableEntry kColumnFillKTable[]; + static const KTableEntry kBoxPropSourceKTable[]; + static const KTableEntry kBoxShadowTypeKTable[]; + static const KTableEntry kBoxSizingKTable[]; + static const KTableEntry kCaptionSideKTable[]; + // Not const because we modify its entries when the pref + // "layout.css.float-logical-values.enabled" changes: + static KTableEntry kClearKTable[]; + static const KTableEntry kColorKTable[]; + static const KTableEntry kContentKTable[]; + static const KTableEntry kControlCharacterVisibilityKTable[]; + static const KTableEntry kCursorKTable[]; + static const KTableEntry kDirectionKTable[]; + // Not const because we modify its entries when various + // "layout.css.*.enabled" prefs changes: + static KTableEntry kDisplayKTable[]; + static const KTableEntry kElevationKTable[]; + static const KTableEntry kEmptyCellsKTable[]; + // -- tables for parsing the {align,justify}-{content,items,self} properties -- + static const KTableEntry kAlignAllKeywords[]; + static const KTableEntry kAlignOverflowPosition[]; // + static const KTableEntry kAlignSelfPosition[]; // + static const KTableEntry kAlignLegacy[]; // 'legacy' + static const KTableEntry kAlignLegacyPosition[]; // 'left/right/center' + static const KTableEntry kAlignAutoNormalStretchBaseline[]; // 'auto/normal/stretch/baseline' + static const KTableEntry kAlignNormalStretchBaseline[]; // 'normal/stretch/baseline' + static const KTableEntry kAlignNormalBaseline[]; // 'normal/baseline' + static const KTableEntry kAlignContentDistribution[]; // + static const KTableEntry kAlignContentPosition[]; // + // -- tables for auto-completion of the {align,justify}-{content,items,self} properties -- + static const KTableEntry kAutoCompletionAlignJustifySelf[]; + static const KTableEntry kAutoCompletionAlignItems[]; + static const KTableEntry kAutoCompletionAlignJustifyContent[]; + // ------------------------------------------------------------------ + static const KTableEntry kFlexDirectionKTable[]; + static const KTableEntry kFlexWrapKTable[]; + // Not const because we modify its entries when the pref + // "layout.css.float-logical-values.enabled" changes: + static KTableEntry kFloatKTable[]; + static const KTableEntry kFloatEdgeKTable[]; + static const KTableEntry kFontDisplayKTable[]; + static const KTableEntry kFontKTable[]; + static const KTableEntry kFontKerningKTable[]; + static const KTableEntry kFontSizeKTable[]; + static const KTableEntry kFontSmoothingKTable[]; + static const KTableEntry kFontStretchKTable[]; + static const KTableEntry kFontStyleKTable[]; + static const KTableEntry kFontSynthesisKTable[]; + static const KTableEntry kFontVariantKTable[]; + static const KTableEntry kFontVariantAlternatesKTable[]; + static const KTableEntry kFontVariantAlternatesFuncsKTable[]; + static const KTableEntry kFontVariantCapsKTable[]; + static const KTableEntry kFontVariantEastAsianKTable[]; + static const KTableEntry kFontVariantLigaturesKTable[]; + static const KTableEntry kFontVariantNumericKTable[]; + static const KTableEntry kFontVariantPositionKTable[]; + static const KTableEntry kFontWeightKTable[]; + static const KTableEntry kGridAutoFlowKTable[]; + static const KTableEntry kGridTrackBreadthKTable[]; + static const KTableEntry kHyphensKTable[]; + static const KTableEntry kImageOrientationKTable[]; + static const KTableEntry kImageOrientationFlipKTable[]; + static const KTableEntry kIsolationKTable[]; + static const KTableEntry kIMEModeKTable[]; + static const KTableEntry kLineHeightKTable[]; + static const KTableEntry kListStylePositionKTable[]; + static const KTableEntry kListStyleKTable[]; + static const KTableEntry kMaskTypeKTable[]; + static const KTableEntry kMathVariantKTable[]; + static const KTableEntry kMathDisplayKTable[]; + static const KTableEntry kContainKTable[]; + static const KTableEntry kContextOpacityKTable[]; + static const KTableEntry kContextPatternKTable[]; + static const KTableEntry kObjectFitKTable[]; + static const KTableEntry kOrientKTable[]; + static const KTableEntry kOutlineStyleKTable[]; + static const KTableEntry kOverflowKTable[]; + static const KTableEntry kOverflowSubKTable[]; + static const KTableEntry kOverflowClipBoxKTable[]; + static const KTableEntry kOverflowWrapKTable[]; + static const KTableEntry kPageBreakKTable[]; + static const KTableEntry kPageBreakInsideKTable[]; + static const KTableEntry kPageMarksKTable[]; + static const KTableEntry kPageSizeKTable[]; + static const KTableEntry kPitchKTable[]; + static const KTableEntry kPointerEventsKTable[]; + static const KTableEntry kPositionKTable[]; + static const KTableEntry kRadialGradientShapeKTable[]; + static const KTableEntry kRadialGradientSizeKTable[]; + static const KTableEntry kRadialGradientLegacySizeKTable[]; + static const KTableEntry kResizeKTable[]; + static const KTableEntry kRubyAlignKTable[]; + static const KTableEntry kRubyPositionKTable[]; + static const KTableEntry kScrollBehaviorKTable[]; + static const KTableEntry kScrollSnapTypeKTable[]; + static const KTableEntry kSpeakKTable[]; + static const KTableEntry kSpeakHeaderKTable[]; + static const KTableEntry kSpeakNumeralKTable[]; + static const KTableEntry kSpeakPunctuationKTable[]; + static const KTableEntry kSpeechRateKTable[]; + static const KTableEntry kStackSizingKTable[]; + static const KTableEntry kTableLayoutKTable[]; + // Not const because we modify its entries when the pref + // "layout.css.text-align-unsafe-value.enabled" changes: + static KTableEntry kTextAlignKTable[]; + static KTableEntry kTextAlignLastKTable[]; + static const KTableEntry kTextCombineUprightKTable[]; + static const KTableEntry kTextDecorationLineKTable[]; + static const KTableEntry kTextDecorationStyleKTable[]; + static const KTableEntry kTextEmphasisPositionKTable[]; + static const KTableEntry kTextEmphasisStyleFillKTable[]; + static const KTableEntry kTextEmphasisStyleShapeKTable[]; + static const KTableEntry kTextOrientationKTable[]; + static const KTableEntry kTextOverflowKTable[]; + static const KTableEntry kTextTransformKTable[]; + static const KTableEntry kTouchActionKTable[]; + static const KTableEntry kTopLayerKTable[]; + static const KTableEntry kTransformBoxKTable[]; + static const KTableEntry kTransitionTimingFunctionKTable[]; + static const KTableEntry kUnicodeBidiKTable[]; + static const KTableEntry kUserFocusKTable[]; + static const KTableEntry kUserInputKTable[]; + static const KTableEntry kUserModifyKTable[]; + static const KTableEntry kUserSelectKTable[]; + static const KTableEntry kVerticalAlignKTable[]; + static const KTableEntry kVisibilityKTable[]; + static const KTableEntry kVolumeKTable[]; + static const KTableEntry kWhitespaceKTable[]; + static const KTableEntry kWidthKTable[]; // also min-width, max-width + static const KTableEntry kWindowDraggingKTable[]; + static const KTableEntry kWindowShadowKTable[]; + static const KTableEntry kWordBreakKTable[]; + static const KTableEntry kWritingModeKTable[]; +}; + +#endif /* nsCSSProps_h___ */ diff --git a/layout/style/nsCSSPropsGenerated.inc.in b/layout/style/nsCSSPropsGenerated.inc.in new file mode 100644 index 000000000..572342c93 --- /dev/null +++ b/layout/style/nsCSSPropsGenerated.inc.in @@ -0,0 +1,17 @@ +/* -*- 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/. */ + +/* processed file that defines CSS property tables that can't be generated + with the pre-processor, designed to be #included in nsCSSProps.cpp */ + +const char* const nsCSSProps::kIDLNameTable[eCSSProperty_COUNT] = { +${idl_names} +}; + +const int32_t nsCSSProps::kIDLNameSortPositionTable[eCSSProperty_COUNT] = { +${idl_name_positions} +}; + +${assertions} diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h new file mode 100644 index 000000000..701578338 --- /dev/null +++ b/layout/style/nsCSSPseudoClassList.h @@ -0,0 +1,253 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-classes */ + +/* + * This file contains the list of nsIAtoms and their values for CSS + * pseudo-classes. It is designed to be used as inline input to + * nsCSSPseudoClasses.cpp *only* through the magic of C preprocessing. + * All entries must be enclosed in the macros CSS_PSEUDO_CLASS, + * CSS_STATE_DEPENDENT_PSEUDO_CLASS, or CSS_STATE_PSEUDO_CLASS which + * will have cruel and unusual things done to them. The entries should + * be kept in some sort of logical order. The common arguments to these + * macros are: + * name_ : The C++ identifier used for the atom (which will be a member + * of nsCSSPseudoClasses) + * value_ : The pseudo-class as a string, including the initial colon, + * used as the string value of the atom. + * flags_ : A bitfield containing flags defined in nsCSSPseudoClasses.h + * pref_ : The name of the preference controlling whether the + * pseudo-class is recognized by the parser, or the empty + * string if it's unconditional. + * CSS_STATE_PSEUDO_CLASS has an additional argument: + * bit_ : The event state bit or bits that corresponds to the + * pseudo-class, i.e., causes it to match (only one bit + * required to match). + * CSS_STATE_DEPENDENT_PSEUDO_CLASS has an additional argument: + * bit_ : The event state bits that affect whether the pseudo-class + * matches. Matching depends on a customized per-class + * algorithm which should be defined in SelectorMatches() in + * nsCSSRuleProcessor.cpp. + * + * If CSS_STATE_PSEUDO_CLASS is not defined, it'll be automatically + * defined to CSS_STATE_DEPENDENT_PSEUDO_CLASS; + * if CSS_STATE_DEPENDENT_PSEUDO_CLASS is not defined, it'll be + * automatically defined to CSS_PSEUDO_CLASS. + */ + +// OUTPUT_CLASS=nsCSSPseudoClasses +// MACRO_NAME=CSS_PSEUDO_CLASS + +#ifdef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS +#error "CSS_STATE_DEPENDENT_PSEUDO_CLASS shouldn't be defined" +#endif + +#ifndef CSS_STATE_DEPENDENT_PSEUDO_CLASS +#define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _flags, _pref, _bit) \ + CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) +#define DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS +#endif + +#ifdef DEFINED_CSS_STATE_PSEUDO_CLASS +#error "CSS_STATE_PSEUDO_CLASS shouldn't be defined" +#endif + +#ifndef CSS_STATE_PSEUDO_CLASS +#define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _bit) \ + CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _flags, _pref, _bit) +#define DEFINED_CSS_STATE_PSEUDO_CLASS +#endif + +// The CSS_PSEUDO_CLASS entries should all come before the +// CSS_STATE_PSEUDO_CLASS entries. The CSS_PSEUDO_CLASS entry order +// must be the same as the order of cases in SelectorMatches. :not +// must be the last CSS_PSEUDO_CLASS. + +CSS_PSEUDO_CLASS(empty, ":empty", 0, "") +CSS_PSEUDO_CLASS(mozOnlyWhitespace, ":-moz-only-whitespace", 0, "") +CSS_PSEUDO_CLASS(mozEmptyExceptChildrenWithLocalname, ":-moz-empty-except-children-with-localname", 0, "") +CSS_PSEUDO_CLASS(lang, ":lang", 0, "") +CSS_PSEUDO_CLASS(mozBoundElement, ":-moz-bound-element", 0, "") +CSS_PSEUDO_CLASS(root, ":root", 0, "") +CSS_PSEUDO_CLASS(any, ":-moz-any", 0, "") + +CSS_PSEUDO_CLASS(firstChild, ":first-child", 0, "") +CSS_PSEUDO_CLASS(firstNode, ":-moz-first-node", 0, "") +CSS_PSEUDO_CLASS(lastChild, ":last-child", 0, "") +CSS_PSEUDO_CLASS(lastNode, ":-moz-last-node", 0, "") +CSS_PSEUDO_CLASS(onlyChild, ":only-child", 0, "") +CSS_PSEUDO_CLASS(firstOfType, ":first-of-type", 0, "") +CSS_PSEUDO_CLASS(lastOfType, ":last-of-type", 0, "") +CSS_PSEUDO_CLASS(onlyOfType, ":only-of-type", 0, "") +CSS_PSEUDO_CLASS(nthChild, ":nth-child", 0, "") +CSS_PSEUDO_CLASS(nthLastChild, ":nth-last-child", 0, "") +CSS_PSEUDO_CLASS(nthOfType, ":nth-of-type", 0, "") +CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "") + +// Match nodes that are HTML but not XHTML +CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "") + +// Match all custom elements whose created callback has not yet been invoked + CSS_STATE_PSEUDO_CLASS(unresolved, ":unresolved", 0, "", NS_EVENT_STATE_UNRESOLVED) + +// Matches nodes that are in a native-anonymous subtree (i.e., nodes in +// a subtree of C++ anonymous content constructed by Gecko for its own +// purposes). +CSS_PSEUDO_CLASS(mozNativeAnonymous, ":-moz-native-anonymous", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "") + +// Matches anything when the specified look-and-feel metric is set +CSS_PSEUDO_CLASS(mozSystemMetric, ":-moz-system-metric", 0, "") + +// -moz-locale-dir(ltr) and -moz-locale-dir(rtl) may be used +// to match based on the locale's chrome direction +CSS_PSEUDO_CLASS(mozLocaleDir, ":-moz-locale-dir", 0, "") + +// -moz-lwtheme may be used to match a document that has a lightweight theme +CSS_PSEUDO_CLASS(mozLWTheme, ":-moz-lwtheme", 0, "") + +// -moz-lwtheme-brighttext matches a document that has a dark lightweight theme +CSS_PSEUDO_CLASS(mozLWThemeBrightText, ":-moz-lwtheme-brighttext", 0, "") + +// -moz-lwtheme-darktext matches a document that has a bright lightweight theme +CSS_PSEUDO_CLASS(mozLWThemeDarkText, ":-moz-lwtheme-darktext", 0, "") + +// Matches anything when the containing window is inactive +CSS_PSEUDO_CLASS(mozWindowInactive, ":-moz-window-inactive", 0, "") + +// Matches any table elements that have a nonzero border attribute, +// according to HTML integer attribute parsing rules. +CSS_PSEUDO_CLASS(mozTableBorderNonzero, ":-moz-table-border-nonzero", 0, "") + +// Matches HTML frame/iframe elements which are mozbrowser. +CSS_PSEUDO_CLASS(mozBrowserFrame, ":-moz-browser-frame", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "") + +// Matches whatever the contextual reference elements are for the +// matching operation. +CSS_PSEUDO_CLASS(scope, ":scope", 0, "layout.css.scope-pseudo.enabled") + +// :not needs to come at the end of the non-bit pseudo-class list, since +// it doesn't actually get directly matched on in SelectorMatches. +CSS_PSEUDO_CLASS(negation, ":not", 0, "") + +// :dir(ltr) and :dir(rtl) match elements whose resolved +// directionality in the markup language is ltr or rtl respectively. +CSS_STATE_DEPENDENT_PSEUDO_CLASS(dir, ":dir", 0, "", + NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL) +// prefix version is deprecated and will be removed per bug 1270406. +CSS_STATE_DEPENDENT_PSEUDO_CLASS(mozDir, ":-moz-dir", 0, "", + NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL) + +CSS_STATE_PSEUDO_CLASS(link, ":link", 0, "", NS_EVENT_STATE_UNVISITED) +// what matches :link or :visited +CSS_STATE_PSEUDO_CLASS(mozAnyLink, ":-moz-any-link", 0, "", + NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED) +CSS_STATE_PSEUDO_CLASS(anyLink, ":any-link", 0, "", + NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED) +CSS_STATE_PSEUDO_CLASS(visited, ":visited", 0, "", NS_EVENT_STATE_VISITED) + +CSS_STATE_PSEUDO_CLASS(active, ":active", 0, "", NS_EVENT_STATE_ACTIVE) +CSS_STATE_PSEUDO_CLASS(checked, ":checked", 0, "", NS_EVENT_STATE_CHECKED) +CSS_STATE_PSEUDO_CLASS(disabled, ":disabled", 0, "", NS_EVENT_STATE_DISABLED) +CSS_STATE_PSEUDO_CLASS(enabled, ":enabled", 0, "", NS_EVENT_STATE_ENABLED) +CSS_STATE_PSEUDO_CLASS(focus, ":focus", 0, "", NS_EVENT_STATE_FOCUS) +CSS_STATE_PSEUDO_CLASS(focusWithin, ":focus-within", 0, "", NS_EVENT_STATE_FOCUS_WITHIN) +CSS_STATE_PSEUDO_CLASS(hover, ":hover", 0, "", NS_EVENT_STATE_HOVER) +CSS_STATE_PSEUDO_CLASS(mozDragOver, ":-moz-drag-over", 0, "", NS_EVENT_STATE_DRAGOVER) +CSS_STATE_PSEUDO_CLASS(target, ":target", 0, "", NS_EVENT_STATE_URLTARGET) +CSS_STATE_PSEUDO_CLASS(indeterminate, ":indeterminate", 0, "", + NS_EVENT_STATE_INDETERMINATE) + +CSS_STATE_PSEUDO_CLASS(mozDevtoolsHighlighted, ":-moz-devtools-highlighted", 0, "", + NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED) +CSS_STATE_PSEUDO_CLASS(mozStyleeditorTransitioning, ":-moz-styleeditor-transitioning", 0, "", + NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING) + +// Matches the element which is being displayed full-screen, and +// any containing frames. +CSS_STATE_PSEUDO_CLASS(fullscreen, ":fullscreen", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, + "full-screen-api.unprefix.enabled", + NS_EVENT_STATE_FULL_SCREEN) +CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", 0, "", NS_EVENT_STATE_FULL_SCREEN) + +// Matches if the element is focused and should show a focus ring +CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", 0, "", NS_EVENT_STATE_FOCUSRING) + +// Image, object, etc state pseudo-classes +CSS_STATE_PSEUDO_CLASS(mozBroken, ":-moz-broken", 0, "", NS_EVENT_STATE_BROKEN) +CSS_STATE_PSEUDO_CLASS(mozLoading, ":-moz-loading", 0, "", NS_EVENT_STATE_LOADING) + +CSS_STATE_PSEUDO_CLASS(mozUserDisabled, ":-moz-user-disabled", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_USERDISABLED) +CSS_STATE_PSEUDO_CLASS(mozSuppressed, ":-moz-suppressed", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_SUPPRESSED) +CSS_STATE_PSEUDO_CLASS(mozHandlerClickToPlay, ":-moz-handler-clicktoplay", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_TYPE_CLICK_TO_PLAY) +CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableUpdatable, ":-moz-handler-vulnerable-updatable", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_VULNERABLE_UPDATABLE) +CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableNoUpdate, ":-moz-handler-vulnerable-no-update", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_VULNERABLE_NO_UPDATE) +CSS_STATE_PSEUDO_CLASS(mozHandlerDisabled, ":-moz-handler-disabled", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_HANDLER_DISABLED) +CSS_STATE_PSEUDO_CLASS(mozHandlerBlocked, ":-moz-handler-blocked", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_HANDLER_BLOCKED) +CSS_STATE_PSEUDO_CLASS(mozHandlerCrashed, ":-moz-handler-crashed", + CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "", + NS_EVENT_STATE_HANDLER_CRASHED) + +CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel, + ":-moz-math-increment-script-level", 0, "", + NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL) + +// CSS 3 UI +// http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes +CSS_STATE_PSEUDO_CLASS(required, ":required", 0, "", NS_EVENT_STATE_REQUIRED) +CSS_STATE_PSEUDO_CLASS(optional, ":optional", 0, "", NS_EVENT_STATE_OPTIONAL) +CSS_STATE_PSEUDO_CLASS(valid, ":valid", 0, "", NS_EVENT_STATE_VALID) +CSS_STATE_PSEUDO_CLASS(invalid, ":invalid", 0, "", NS_EVENT_STATE_INVALID) +CSS_STATE_PSEUDO_CLASS(inRange, ":in-range", 0, "", NS_EVENT_STATE_INRANGE) +CSS_STATE_PSEUDO_CLASS(outOfRange, ":out-of-range", 0, "", NS_EVENT_STATE_OUTOFRANGE) +CSS_STATE_PSEUDO_CLASS(defaultPseudo, ":default", 0, "", NS_EVENT_STATE_DEFAULT) +CSS_STATE_PSEUDO_CLASS(placeholderShown, ":placeholder-shown", 0, "", + NS_EVENT_STATE_PLACEHOLDERSHOWN) +CSS_STATE_PSEUDO_CLASS(mozReadOnly, ":-moz-read-only", 0, "", + NS_EVENT_STATE_MOZ_READONLY) +CSS_STATE_PSEUDO_CLASS(mozReadWrite, ":-moz-read-write", 0, "", + NS_EVENT_STATE_MOZ_READWRITE) +CSS_STATE_PSEUDO_CLASS(mozSubmitInvalid, ":-moz-submit-invalid", 0, "", + NS_EVENT_STATE_MOZ_SUBMITINVALID) +CSS_STATE_PSEUDO_CLASS(mozUIInvalid, ":-moz-ui-invalid", 0, "", + NS_EVENT_STATE_MOZ_UI_INVALID) +CSS_STATE_PSEUDO_CLASS(mozUIValid, ":-moz-ui-valid", 0, "", + NS_EVENT_STATE_MOZ_UI_VALID) +CSS_STATE_PSEUDO_CLASS(mozMeterOptimum, ":-moz-meter-optimum", 0, "", + NS_EVENT_STATE_OPTIMUM) +CSS_STATE_PSEUDO_CLASS(mozMeterSubOptimum, ":-moz-meter-sub-optimum", 0, "", + NS_EVENT_STATE_SUB_OPTIMUM) +CSS_STATE_PSEUDO_CLASS(mozMeterSubSubOptimum, ":-moz-meter-sub-sub-optimum", 0, "", + NS_EVENT_STATE_SUB_SUB_OPTIMUM) + +// Those values should be parsed but do nothing. +CSS_STATE_PSEUDO_CLASS(mozPlaceholder, ":-moz-placeholder", 0, "", NS_EVENT_STATE_IGNORE) + +#ifdef DEFINED_CSS_STATE_PSEUDO_CLASS +#undef DEFINED_CSS_STATE_PSEUDO_CLASS +#undef CSS_STATE_PSEUDO_CLASS +#endif + +#ifdef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS +#undef DEFINED_CSS_STATE_DEPENDENT_PSEUDO_CLASS +#undef CSS_STATE_DEPENDENT_PSEUDO_CLASS +#endif diff --git a/layout/style/nsCSSPseudoClasses.cpp b/layout/style/nsCSSPseudoClasses.cpp new file mode 100644 index 000000000..9b26459dd --- /dev/null +++ b/layout/style/nsCSSPseudoClasses.cpp @@ -0,0 +1,131 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-classes */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCSSPseudoClasses.h" +#include "nsStaticAtom.h" +#include "mozilla/Preferences.h" +#include "nsString.h" + +using namespace mozilla; + +// define storage for all atoms +#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ + static nsIAtom* sPseudoClass_##_name; +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS + +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + NS_STATIC_ATOM_BUFFER(name_##_pseudo_class_buffer, value_) +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS + +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + static_assert(!((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_CHROME) || \ + ((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), \ + "Pseudo-class '" #name_ "' is enabled in chrome, so it " \ + "should also be enabled in UA sheets"); +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS + +// Array of nsStaticAtom for each of the pseudo-classes. +static const nsStaticAtom CSSPseudoClasses_info[] = { +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + NS_STATIC_ATOM(name_##_pseudo_class_buffer, &sPseudoClass_##name_), +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS +}; + +// Flags data for each of the pseudo-classes, which must be separate +// from the previous array since there's no place for it in +// nsStaticAtom. +/* static */ const uint32_t +nsCSSPseudoClasses::kPseudoClassFlags[] = { +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + flags_, +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS +}; + +/* static */ bool +nsCSSPseudoClasses::sPseudoClassEnabled[] = { +// If the pseudo class has any "ENABLED_IN" flag set, it is disabled by +// default. Note that, if a pseudo class has pref, whatever its default +// value is, it'll later be changed in nsCSSPseudoClasses::AddRefAtoms() +// If the pseudo class has "ENABLED_IN" flags but doesn't have a pref, +// it is an internal pseudo class which is disabled elsewhere. +#define IS_ENABLED_BY_DEFAULT(flags_) \ + (!((flags_) & CSS_PSEUDO_CLASS_ENABLED_MASK)) +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + IS_ENABLED_BY_DEFAULT(flags_), +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS +#undef IS_ENABLED_BY_DEFAULT +}; + +void nsCSSPseudoClasses::AddRefAtoms() +{ + NS_RegisterStaticAtoms(CSSPseudoClasses_info); + +#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \ + if (pref_[0]) { \ + auto idx = static_cast(Type::name_); \ + Preferences::AddBoolVarCache(&sPseudoClassEnabled[idx], pref_); \ + } +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS +} + +bool +nsCSSPseudoClasses::HasStringArg(Type aType) +{ + return aType == Type::lang || + aType == Type::mozEmptyExceptChildrenWithLocalname || + aType == Type::mozSystemMetric || + aType == Type::mozLocaleDir || + aType == Type::mozDir || + aType == Type::dir; +} + +bool +nsCSSPseudoClasses::HasNthPairArg(Type aType) +{ + return aType == Type::nthChild || + aType == Type::nthLastChild || + aType == Type::nthOfType || + aType == Type::nthLastOfType; +} + +void +nsCSSPseudoClasses::PseudoTypeToString(Type aType, nsAString& aString) +{ + MOZ_ASSERT(aType < Type::Count, "Unexpected type"); + auto idx = static_cast(aType); + (*CSSPseudoClasses_info[idx].mAtom)->ToString(aString); +} + +/* static */ CSSPseudoClassType +nsCSSPseudoClasses::GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState) +{ + for (uint32_t i = 0; i < ArrayLength(CSSPseudoClasses_info); ++i) { + if (*CSSPseudoClasses_info[i].mAtom == aAtom) { + Type type = Type(i); + return IsEnabled(type, aEnabledState) ? type : Type::NotPseudo; + } + } + return Type::NotPseudo; +} + +/* static */ bool +nsCSSPseudoClasses::IsUserActionPseudoClass(Type aType) +{ + // See http://dev.w3.org/csswg/selectors4/#useraction-pseudos + return aType == Type::hover || + aType == Type::active || + aType == Type::focus; +} diff --git a/layout/style/nsCSSPseudoClasses.h b/layout/style/nsCSSPseudoClasses.h new file mode 100644 index 000000000..ca1cb2f39 --- /dev/null +++ b/layout/style/nsCSSPseudoClasses.h @@ -0,0 +1,90 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-classes */ + +#ifndef nsCSSPseudoClasses_h___ +#define nsCSSPseudoClasses_h___ + +#include "nsStringFwd.h" + +// The following two flags along with the pref defines where this pseudo +// class can be used: +// * If none of the two flags is presented, the pref completely controls +// the availability of this pseudo class. And in that case, if it has +// no pref, this property is usable everywhere. +// * If any of the flags is set, this pseudo class is always enabled in +// the specific contexts regardless of the value of the pref. If there +// is no pref for this pseudo class at all in this case, it is an +// internal-only pseudo class, which cannot be used anywhere else. +#define CSS_PSEUDO_CLASS_ENABLED_MASK (3<<0) +#define CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS (1<<0) +#define CSS_PSEUDO_CLASS_ENABLED_IN_CHROME (1<<1) +#define CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME \ + (CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS | CSS_PSEUDO_CLASS_ENABLED_IN_CHROME) + +class nsIAtom; + +namespace mozilla { + +// The total count of CSSPseudoClassType is less than 256, +// so use uint8_t as its underlying type. +typedef uint8_t CSSPseudoClassTypeBase; +enum class CSSPseudoClassType : CSSPseudoClassTypeBase +{ +#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ + _name, +#include "nsCSSPseudoClassList.h" +#undef CSS_PSEUDO_CLASS + Count, + NotPseudo, // This value MUST be second last! SelectorMatches depends on it. + MAX +}; + +} // namespace mozilla + +class nsCSSPseudoClasses +{ + typedef mozilla::CSSPseudoClassType Type; + typedef mozilla::CSSEnabledState EnabledState; + +public: + static void AddRefAtoms(); + + static Type GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState); + static bool HasStringArg(Type aType); + static bool HasNthPairArg(Type aType); + static bool HasSelectorListArg(Type aType) { + return aType == Type::any; + } + static bool IsUserActionPseudoClass(Type aType); + + // Should only be used on types other than Count and NotPseudoClass + static void PseudoTypeToString(Type aType, nsAString& aString); + + static bool IsEnabled(Type aType, EnabledState aEnabledState) + { + auto index = static_cast(aType); + MOZ_ASSERT(index < static_cast(Type::Count)); + if (sPseudoClassEnabled[index] || + aEnabledState == EnabledState::eIgnoreEnabledState) { + return true; + } + auto flags = kPseudoClassFlags[index]; + if (((aEnabledState & EnabledState::eInChrome) && + (flags & CSS_PSEUDO_CLASS_ENABLED_IN_CHROME)) || + ((aEnabledState & EnabledState::eInUASheets) && + (flags & CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS))) { + return true; + } + return false; + } + +private: + static const uint32_t kPseudoClassFlags[size_t(Type::Count)]; + static bool sPseudoClassEnabled[size_t(Type::Count)]; +}; + +#endif /* nsCSSPseudoClasses_h___ */ diff --git a/layout/style/nsCSSPseudoElementList.h b/layout/style/nsCSSPseudoElementList.h new file mode 100644 index 000000000..552c76734 --- /dev/null +++ b/layout/style/nsCSSPseudoElementList.h @@ -0,0 +1,87 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-elements */ + +/* + * This file contains the list of nsIAtoms and their values for CSS + * pseudo-elements. 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 + */ + +// OUTPUT_CLASS=nsCSSPseudoElements +// MACRO_NAME=CSS_PSEUDO_ELEMENT + +CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2) +CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2) + +CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0) + +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(mozSelection, ":-moz-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) +CSS_PSEUDO_ELEMENT(mozFocusOuter, ":-moz-focus-outer", 0) + +// XXXbz should we really allow random content to style these? Maybe +// use our flags to prevent that? +CSS_PSEUDO_ELEMENT(mozListBullet, ":-moz-list-bullet", 0) +CSS_PSEUDO_ELEMENT(mozListNumber, ":-moz-list-number", 0) + +CSS_PSEUDO_ELEMENT(mozMathAnonymous, ":-moz-math-anonymous", 0) + +// HTML5 Forms pseudo elements +CSS_PSEUDO_ELEMENT(mozNumberWrapper, ":-moz-number-wrapper", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) +CSS_PSEUDO_ELEMENT(mozNumberText, ":-moz-number-text", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) +CSS_PSEUDO_ELEMENT(mozNumberSpinBox, ":-moz-number-spin-box", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) +CSS_PSEUDO_ELEMENT(mozNumberSpinUp, ":-moz-number-spin-up", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) +CSS_PSEUDO_ELEMENT(mozNumberSpinDown, ":-moz-number-spin-down", + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE | + CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) +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(mozPlaceholder, ":-moz-placeholder", + 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) diff --git a/layout/style/nsCSSPseudoElements.cpp b/layout/style/nsCSSPseudoElements.cpp new file mode 100644 index 000000000..ef09f2d42 --- /dev/null +++ b/layout/style/nsCSSPseudoElements.cpp @@ -0,0 +1,120 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-elements */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCSSPseudoElements.h" +#include "nsAtomListUtils.h" +#include "nsStaticAtom.h" +#include "nsCSSAnonBoxes.h" + +using namespace mozilla; + +// define storage for all atoms +#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \ + nsICSSPseudoElement* nsCSSPseudoElements::name_; +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT + +#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \ + NS_STATIC_ATOM_BUFFER(name_##_pseudo_element_buffer, value_) +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT + +// Array of nsStaticAtom for each of the pseudo-elements. +static const nsStaticAtom CSSPseudoElements_info[] = { +#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \ + NS_STATIC_ATOM(name_##_pseudo_element_buffer, (nsIAtom**)&nsCSSPseudoElements::name_), +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT +}; + +// Flags data for each of the pseudo-elements, which must be separate +// from the previous array since there's no place for it in +// nsStaticAtom. +/* static */ const uint32_t +nsCSSPseudoElements::kPseudoElementFlags[] = { +#define CSS_PSEUDO_ELEMENT(name_, value_, flags_) \ + flags_, +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT +}; + +void nsCSSPseudoElements::AddRefAtoms() +{ + NS_RegisterStaticAtoms(CSSPseudoElements_info); +} + +bool nsCSSPseudoElements::IsPseudoElement(nsIAtom *aAtom) +{ + return nsAtomListUtils::IsMember(aAtom, CSSPseudoElements_info, + ArrayLength(CSSPseudoElements_info)); +} + +/* static */ bool +nsCSSPseudoElements::IsCSS2PseudoElement(nsIAtom *aAtom) +{ + // We don't implement this using PseudoElementHasFlags because callers + // want to pass things that could be anon boxes. + NS_ASSERTION(nsCSSPseudoElements::IsPseudoElement(aAtom) || + nsCSSAnonBoxes::IsAnonBox(aAtom), + "must be pseudo element or anon box"); + bool result = aAtom == nsCSSPseudoElements::after || + aAtom == nsCSSPseudoElements::before || + aAtom == nsCSSPseudoElements::firstLetter || + aAtom == nsCSSPseudoElements::firstLine; + NS_ASSERTION(nsCSSAnonBoxes::IsAnonBox(aAtom) || + result == PseudoElementHasFlags( + GetPseudoType(aAtom, EnabledState::eIgnoreEnabledState), + CSS_PSEUDO_ELEMENT_IS_CSS2), + "result doesn't match flags"); + return result; +} + +/* static */ CSSPseudoElementType +nsCSSPseudoElements::GetPseudoType(nsIAtom *aAtom, EnabledState aEnabledState) +{ + for (CSSPseudoElementTypeBase i = 0; + i < ArrayLength(CSSPseudoElements_info); + ++i) { + if (*CSSPseudoElements_info[i].mAtom == aAtom) { + auto type = static_cast(i); + // ::moz-placeholder is an alias for ::placeholder + if (type == CSSPseudoElementType::mozPlaceholder) { + type = CSSPseudoElementType::placeholder; + } + return IsEnabled(type, aEnabledState) ? type : Type::NotPseudo; + } + } + + if (nsCSSAnonBoxes::IsAnonBox(aAtom)) { +#ifdef MOZ_XUL + if (nsCSSAnonBoxes::IsTreePseudoElement(aAtom)) { + return Type::XULTree; + } +#endif + + return Type::AnonBox; + } + + return Type::NotPseudo; +} + +/* static */ nsIAtom* +nsCSSPseudoElements::GetPseudoAtom(Type aType) +{ + NS_ASSERTION(aType < Type::Count, "Unexpected type"); + return *CSSPseudoElements_info[ + static_cast(aType)].mAtom; +} + +/* static */ bool +nsCSSPseudoElements::PseudoElementSupportsUserActionState(const Type aType) +{ + return PseudoElementHasFlags(aType, + CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE); +} diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h new file mode 100644 index 000000000..657ef663e --- /dev/null +++ b/layout/style/nsCSSPseudoElements.h @@ -0,0 +1,128 @@ +/* -*- 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/. */ + +/* atom list for CSS pseudo-elements */ + +#ifndef nsCSSPseudoElements_h___ +#define nsCSSPseudoElements_h___ + +#include "nsIAtom.h" +#include "mozilla/CSSEnabledState.h" +#include "mozilla/Compiler.h" + +// Is this pseudo-element a CSS2 pseudo-element that can be specified +// with the single colon syntax (in addition to the double-colon syntax, +// which can be used for all pseudo-elements)? +#define CSS_PSEUDO_ELEMENT_IS_CSS2 (1<<0) +// Is this pseudo-element a pseudo-element that can contain other +// elements? +// (Currently pseudo-elements are either leaves of the tree (relative to +// real elements) or they contain other elements in a non-tree-like +// manner (i.e., like incorrectly-nested start and end tags). It's +// possible that in the future there might be container pseudo-elements +// that form a properly nested tree structure. If that happens, we +// should probably split this flag into two.) +#define CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS (1<<1) +// Flag to add the ability to take into account style attribute set for the +// pseudo element (by default it's ignored). +#define CSS_PSEUDO_ELEMENT_SUPPORTS_STYLE_ATTRIBUTE (1<<2) +// Flag that indicate the pseudo-element supports a user action pseudo-class +// following it, such as :active or :hover. This would normally correspond +// to whether the pseudo-element is tree-like, but we don't support these +// pseudo-classes on ::before and ::after generated content yet. See +// http://dev.w3.org/csswg/selectors4/#pseudo-elements. +#define CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (1<<3) +// Is content prevented from parsing selectors containing this pseudo-element? +#define CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY (1<<4) + +namespace mozilla { + +// The total count of CSSPseudoElement is less than 256, +// so use uint8_t as its underlying type. +typedef uint8_t CSSPseudoElementTypeBase; +enum class CSSPseudoElementType : CSSPseudoElementTypeBase { + // If the actual pseudo-elements stop being first here, change + // GetPseudoType. +#define CSS_PSEUDO_ELEMENT(_name, _value_, _flags) \ + _name, +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT + Count, + AnonBox = Count, +#ifdef MOZ_XUL + XULTree, +#endif + NotPseudo, + MAX +}; + +} // namespace mozilla + +// Empty class derived from nsIAtom so that function signatures can +// require an atom from this atom list. +class nsICSSPseudoElement : public nsIAtom {}; + +class nsCSSPseudoElements +{ + typedef mozilla::CSSPseudoElementType Type; + typedef mozilla::CSSEnabledState EnabledState; + +public: + static void AddRefAtoms(); + + static bool IsPseudoElement(nsIAtom *aAtom); + + static bool IsCSS2PseudoElement(nsIAtom *aAtom); + +#define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \ + static nsICSSPseudoElement* _name; +#include "nsCSSPseudoElementList.h" +#undef CSS_PSEUDO_ELEMENT + + static Type GetPseudoType(nsIAtom* aAtom, EnabledState aEnabledState); + + // Get the atom for a given Type. aType must be < CSSPseudoElementType::Count + static nsIAtom* GetPseudoAtom(Type aType); + + static bool PseudoElementContainsElements(const Type aType) { + return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS); + } + + static bool PseudoElementSupportsStyleAttribute(const Type aType) { + MOZ_ASSERT(aType < Type::Count); + return PseudoElementHasFlags(aType, + CSS_PSEUDO_ELEMENT_SUPPORTS_STYLE_ATTRIBUTE); + } + + static bool PseudoElementSupportsUserActionState(const Type aType); + + static bool IsEnabled(Type aType, EnabledState aEnabledState) + { + return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) || + (aEnabledState & EnabledState::eInUASheets); + } + +private: + // Does the given pseudo-element have all of the flags given? + + // Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 , + // which is a general gcc bug that we seem to have hit only on Android/x86. +#if defined(ANDROID) && defined(__i386__) && defined(__GNUC__) && \ + !defined(__clang__) +#if (MOZ_GCC_VERSION_AT_LEAST(4,8,0) && MOZ_GCC_VERSION_AT_MOST(4,8,4)) || \ + (MOZ_GCC_VERSION_AT_LEAST(4,9,0) && MOZ_GCC_VERSION_AT_MOST(4,9,2)) + __attribute__((noinline)) +#endif +#endif + static bool PseudoElementHasFlags(const Type aType, uint32_t aFlags) + { + MOZ_ASSERT(aType < Type::Count); + return (kPseudoElementFlags[size_t(aType)] & aFlags) == aFlags; + } + + static const uint32_t kPseudoElementFlags[size_t(Type::Count)]; +}; + +#endif /* nsCSSPseudoElements_h___ */ diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp new file mode 100644 index 000000000..07a4ef57b --- /dev/null +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -0,0 +1,4061 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:cindent:tabstop=2:expandtab:shiftwidth=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/. */ + +/* + * style rule processor for CSS style sheets, responsible for selector + * matching and cascading + */ + +#define PL_ARENA_CONST_ALIGN_MASK 7 +// We want page-sized arenas so there's no fragmentation involved. +// Including plarena.h must come first to avoid it being included by some +// header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective. +#define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096) +#include "plarena.h" + +#include "nsAutoPtr.h" +#include "nsCSSRuleProcessor.h" +#include "nsRuleProcessorData.h" +#include +#include "nsIAtom.h" +#include "PLDHashTable.h" +#include "nsICSSPseudoComparator.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/css/StyleRule.h" +#include "mozilla/css/GroupRule.h" +#include "nsIDocument.h" +#include "nsPresContext.h" +#include "nsGkAtoms.h" +#include "nsUnicharUtils.h" +#include "nsError.h" +#include "nsRuleWalker.h" +#include "nsCSSPseudoClasses.h" +#include "nsCSSPseudoElements.h" +#include "nsIContent.h" +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsStyleUtil.h" +#include "nsQuickSort.h" +#include "nsAttrValue.h" +#include "nsAttrValueInlines.h" +#include "nsAttrName.h" +#include "nsTArray.h" +#include "nsContentUtils.h" +#include "nsIMediaList.h" +#include "nsCSSRules.h" +#include "nsStyleSet.h" +#include "mozilla/dom/Element.h" +#include "nsNthIndexCache.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/EventStates.h" +#include "mozilla/Preferences.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/Likely.h" +#include "mozilla/OperatorNewExtensions.h" +#include "mozilla/TypedEnumBits.h" +#include "RuleProcessorCache.h" +#include "nsIDOMMutationEvent.h" +#include "nsIMozBrowserFrame.h" + +using namespace mozilla; +using namespace mozilla::dom; + +#define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled" + +static bool gSupportVisitedPseudo = true; + +static nsTArray< nsCOMPtr >* sSystemMetrics = 0; + +#ifdef XP_WIN +uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic; +#endif + +/** + * A struct representing a given CSS rule and a particular selector + * from that rule's selector list. + */ +struct RuleSelectorPair { + RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector) + : mRule(aRule), mSelector(aSelector) {} + // If this class ever grows a destructor, deal with + // PerWeightDataListItem appropriately. + + css::StyleRule* mRule; + nsCSSSelector* mSelector; // which of |mRule|'s selectors +}; + +#define NS_IS_ANCESTOR_OPERATOR(ch) \ + ((ch) == char16_t(' ') || (ch) == char16_t('>')) + +/** + * A struct representing a particular rule in an ordered list of rules + * (the ordering depending on the weight of mSelector and the order of + * our rules to start with). + */ +struct RuleValue : RuleSelectorPair { + enum { + eMaxAncestorHashes = 4 + }; + + RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex, + bool aQuirksMode) : + RuleSelectorPair(aRuleSelectorPair), + mIndex(aIndex) + { + CollectAncestorHashes(aQuirksMode); + } + + int32_t mIndex; // High index means high weight/order. + uint32_t mAncestorSelectorHashes[eMaxAncestorHashes]; + +private: + void CollectAncestorHashes(bool aQuirksMode) { + // Collect up our mAncestorSelectorHashes. It's not clear whether it's + // better to stop once we've found eMaxAncestorHashes of them or to keep + // going and preferentially collect information from selectors higher up the + // chain... Let's do the former for now. + size_t hashIndex = 0; + for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) { + if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) { + // |sel| is going to select something that's not actually one of our + // ancestors, so don't add it to mAncestorSelectorHashes. But keep + // going, because it'll select a sibling of one of our ancestors, so its + // ancestors would be our ancestors too. + continue; + } + + // Now sel is supposed to select one of our ancestors. Grab + // whatever info we can from it into mAncestorSelectorHashes. + // But in qurks mode, don't grab IDs and classes because those + // need to be matched case-insensitively. + if (!aQuirksMode) { + nsAtomList* ids = sel->mIDList; + while (ids) { + mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash(); + if (hashIndex == eMaxAncestorHashes) { + return; + } + ids = ids->mNext; + } + + nsAtomList* classes = sel->mClassList; + while (classes) { + mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash(); + if (hashIndex == eMaxAncestorHashes) { + return; + } + classes = classes->mNext; + } + } + + // Only put in the tag name if it's all-lowercase. Otherwise we run into + // trouble because we may test the wrong one of mLowercaseTag and + // mCasedTag against the filter. + if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) { + mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash(); + if (hashIndex == eMaxAncestorHashes) { + return; + } + } + } + + while (hashIndex != eMaxAncestorHashes) { + mAncestorSelectorHashes[hashIndex++] = 0; + } + } +}; + +// ------------------------------ +// Rule hash table +// + +// Uses any of the sets of ops below. +struct RuleHashTableEntry : public PLDHashEntryHdr { + // If you add members that have heap allocated memory be sure to change the + // logic in SizeOfRuleHashTable(). + // Auto length 1, because we always have at least one entry in mRules. + AutoTArray mRules; +}; + +struct RuleHashTagTableEntry : public RuleHashTableEntry { + // If you add members that have heap allocated memory be sure to change the + // logic in RuleHash::SizeOf{In,Ex}cludingThis. + nsCOMPtr mTag; +}; + +static PLDHashNumber +RuleHash_CIHashKey(const void *key) +{ + nsIAtom *atom = const_cast(static_cast(key)); + + nsAutoString str; + atom->ToString(str); + nsContentUtils::ASCIIToLower(str); + return HashString(str); +} + +static inline nsCSSSelector* +SubjectSelectorForRuleHash(const PLDHashEntryHdr *hdr) +{ + auto entry = static_cast(hdr); + nsCSSSelector* selector = entry->mRules[0].mSelector; + if (selector->IsPseudoElement()) { + selector = selector->mNext; + } + return selector; +} + +static inline bool +CIMatchAtoms(const void* key, nsIAtom *entry_atom) +{ + auto match_atom = const_cast(static_cast(key)); + + // Check for case-sensitive match first. + if (match_atom == entry_atom) { + return true; + } + + // Use EqualsIgnoreASCIICase instead of full on unicode case conversion + // in order to save on performance. This is only used in quirks mode + // anyway. + return + nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom), + nsDependentAtomString(match_atom)); +} + +static inline bool +CSMatchAtoms(const void* key, nsIAtom *entry_atom) +{ + auto match_atom = const_cast(static_cast(key)); + return match_atom == entry_atom; +} + +static bool +RuleHash_ClassCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom); +} + +static bool +RuleHash_IdCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom); +} + +static bool +RuleHash_ClassCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom); +} + +static bool +RuleHash_IdCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom); +} + +static void +RuleHash_InitEntry(PLDHashEntryHdr *hdr, const void *key) +{ + RuleHashTableEntry* entry = static_cast(hdr); + new (KnownNotNull, entry) RuleHashTableEntry(); +} + +static void +RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + RuleHashTableEntry* entry = static_cast(hdr); + entry->~RuleHashTableEntry(); +} + +static void +RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, + PLDHashEntryHdr *to) +{ + NS_PRECONDITION(from != to, "This is not going to work!"); + RuleHashTableEntry *oldEntry = + const_cast( + static_cast(from)); + auto* newEntry = new (KnownNotNull, to) RuleHashTableEntry(); + newEntry->mRules.SwapElements(oldEntry->mRules); + oldEntry->~RuleHashTableEntry(); +} + +static bool +RuleHash_TagTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + nsIAtom *match_atom = const_cast(static_cast(key)); + nsIAtom *entry_atom = static_cast(hdr)->mTag; + + return match_atom == entry_atom; +} + +static void +RuleHash_TagTable_InitEntry(PLDHashEntryHdr *hdr, const void *key) +{ + RuleHashTagTableEntry* entry = static_cast(hdr); + new (KnownNotNull, entry) RuleHashTagTableEntry(); + entry->mTag = const_cast(static_cast(key)); +} + +static void +RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + RuleHashTagTableEntry* entry = static_cast(hdr); + entry->~RuleHashTagTableEntry(); +} + +static void +RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, + PLDHashEntryHdr *to) +{ + NS_PRECONDITION(from != to, "This is not going to work!"); + RuleHashTagTableEntry *oldEntry = + const_cast( + static_cast(from)); + auto* newEntry = new (KnownNotNull, to) RuleHashTagTableEntry(); + newEntry->mTag.swap(oldEntry->mTag); + newEntry->mRules.SwapElements(oldEntry->mRules); + oldEntry->~RuleHashTagTableEntry(); +} + +static PLDHashNumber +RuleHash_NameSpaceTable_HashKey(const void *key) +{ + return NS_PTR_TO_INT32(key); +} + +static bool +RuleHash_NameSpaceTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + const RuleHashTableEntry *entry = + static_cast(hdr); + + nsCSSSelector* selector = entry->mRules[0].mSelector; + if (selector->IsPseudoElement()) { + selector = selector->mNext; + } + return NS_PTR_TO_INT32(key) == selector->mNameSpace; +} + +static const PLDHashTableOps RuleHash_TagTable_Ops = { + PLDHashTable::HashVoidPtrKeyStub, + RuleHash_TagTable_MatchEntry, + RuleHash_TagTable_MoveEntry, + RuleHash_TagTable_ClearEntry, + RuleHash_TagTable_InitEntry +}; + +// Case-sensitive ops. +static const PLDHashTableOps RuleHash_ClassTable_CSOps = { + PLDHashTable::HashVoidPtrKeyStub, + RuleHash_ClassCSMatchEntry, + RuleHash_MoveEntry, + RuleHash_ClearEntry, + RuleHash_InitEntry +}; + +// Case-insensitive ops. +static const PLDHashTableOps RuleHash_ClassTable_CIOps = { + RuleHash_CIHashKey, + RuleHash_ClassCIMatchEntry, + RuleHash_MoveEntry, + RuleHash_ClearEntry, + RuleHash_InitEntry +}; + +// Case-sensitive ops. +static const PLDHashTableOps RuleHash_IdTable_CSOps = { + PLDHashTable::HashVoidPtrKeyStub, + RuleHash_IdCSMatchEntry, + RuleHash_MoveEntry, + RuleHash_ClearEntry, + RuleHash_InitEntry +}; + +// Case-insensitive ops. +static const PLDHashTableOps RuleHash_IdTable_CIOps = { + RuleHash_CIHashKey, + RuleHash_IdCIMatchEntry, + RuleHash_MoveEntry, + RuleHash_ClearEntry, + RuleHash_InitEntry +}; + +static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = { + RuleHash_NameSpaceTable_HashKey, + RuleHash_NameSpaceTable_MatchEntry, + RuleHash_MoveEntry, + RuleHash_ClearEntry, + RuleHash_InitEntry +}; + +#undef RULE_HASH_STATS +#undef PRINT_UNIVERSAL_RULES + +#ifdef RULE_HASH_STATS +#define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO +#else +#define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO +#endif + +struct NodeMatchContext; + +class RuleHash { +public: + explicit RuleHash(bool aQuirksMode); + ~RuleHash(); + void AppendRule(const RuleSelectorPair &aRuleInfo); + void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData, + NodeMatchContext& aNodeMatchContext); + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + +protected: + typedef nsTArray RuleValueList; + void AppendRuleToTable(PLDHashTable* aTable, const void* aKey, + const RuleSelectorPair& aRuleInfo); + void AppendUniversalRule(const RuleSelectorPair& aRuleInfo); + + int32_t mRuleCount; + + PLDHashTable mIdTable; + PLDHashTable mClassTable; + PLDHashTable mTagTable; + PLDHashTable mNameSpaceTable; + RuleValueList mUniversalRules; + + struct EnumData { + const RuleValue* mCurValue; + const RuleValue* mEnd; + }; + EnumData* mEnumList; + int32_t mEnumListSize; + + bool mQuirksMode; + + inline EnumData ToEnumData(const RuleValueList& arr) { + EnumData data = { arr.Elements(), arr.Elements() + arr.Length() }; + return data; + } + +#ifdef RULE_HASH_STATS + uint32_t mUniversalSelectors; + uint32_t mNameSpaceSelectors; + uint32_t mTagSelectors; + uint32_t mClassSelectors; + uint32_t mIdSelectors; + + uint32_t mElementsMatched; + + uint32_t mElementUniversalCalls; + uint32_t mElementNameSpaceCalls; + uint32_t mElementTagCalls; + uint32_t mElementClassCalls; + uint32_t mElementIdCalls; +#endif // RULE_HASH_STATS +}; + +RuleHash::RuleHash(bool aQuirksMode) + : mRuleCount(0), + mIdTable(aQuirksMode ? &RuleHash_IdTable_CIOps + : &RuleHash_IdTable_CSOps, + sizeof(RuleHashTableEntry)), + mClassTable(aQuirksMode ? &RuleHash_ClassTable_CIOps + : &RuleHash_ClassTable_CSOps, + sizeof(RuleHashTableEntry)), + mTagTable(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), + mNameSpaceTable(&RuleHash_NameSpaceTable_Ops, sizeof(RuleHashTableEntry)), + mUniversalRules(0), + mEnumList(nullptr), mEnumListSize(0), + mQuirksMode(aQuirksMode) +#ifdef RULE_HASH_STATS + , + mUniversalSelectors(0), + mNameSpaceSelectors(0), + mTagSelectors(0), + mClassSelectors(0), + mIdSelectors(0), + mElementsMatched(0), + mElementUniversalCalls(0), + mElementNameSpaceCalls(0), + mElementTagCalls(0), + mElementClassCalls(0), + mElementIdCalls(0) +#endif +{ + MOZ_COUNT_CTOR(RuleHash); +} + +RuleHash::~RuleHash() +{ + MOZ_COUNT_DTOR(RuleHash); +#ifdef RULE_HASH_STATS + printf( +"RuleHash(%p):\n" +" Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n" +" Content Nodes: Elements(%u)\n" +" Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n" + static_cast(this), + mUniversalSelectors, mNameSpaceSelectors, mTagSelectors, + mClassSelectors, mIdSelectors, + mElementsMatched, + mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls, + mElementClassCalls, mElementIdCalls); +#ifdef PRINT_UNIVERSAL_RULES + { + if (mUniversalRules.Length() > 0) { + printf(" Universal rules:\n"); + for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) { + RuleValue* value = &(mUniversalRules[i]); + nsAutoString selectorText; + uint32_t lineNumber = value->mRule->GetLineNumber(); + RefPtr cssSheet = value->mRule->GetStyleSheet(); + value->mSelector->ToString(selectorText, cssSheet); + + printf(" line %d, %s\n", + lineNumber, NS_ConvertUTF16toUTF8(selectorText).get()); + } + } + } +#endif // PRINT_UNIVERSAL_RULES +#endif // RULE_HASH_STATS + // Rule Values are arena allocated no need to delete them. Their destructor + // isn't doing any cleanup. So we dont even bother to enumerate through + // the hash tables and call their destructors. + if (nullptr != mEnumList) { + delete [] mEnumList; + } +} + +void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey, + const RuleSelectorPair& aRuleInfo) +{ + // Get a new or existing entry. + auto entry = static_cast(aTable->Add(aKey, fallible)); + if (!entry) + return; + entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode)); +} + +static void +AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey, + const RuleValue& aRuleInfo) +{ + // Get a new or exisiting entry + auto entry = static_cast(aTable->Add(aKey, fallible)); + if (!entry) + return; + + entry->mRules.AppendElement(aRuleInfo); +} + +void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo) +{ + mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode)); +} + +void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo) +{ + nsCSSSelector *selector = aRuleInfo.mSelector; + if (selector->IsPseudoElement()) { + selector = selector->mNext; + } + if (nullptr != selector->mIDList) { + AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo); + RULE_HASH_STAT_INCREMENT(mIdSelectors); + } + else if (nullptr != selector->mClassList) { + AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo); + RULE_HASH_STAT_INCREMENT(mClassSelectors); + } + else if (selector->mLowercaseTag) { + RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode); + AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue); + RULE_HASH_STAT_INCREMENT(mTagSelectors); + if (selector->mCasedTag && + selector->mCasedTag != selector->mLowercaseTag) { + AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue); + RULE_HASH_STAT_INCREMENT(mTagSelectors); + } + } + else if (kNameSpaceID_Unknown != selector->mNameSpace) { + AppendRuleToTable(&mNameSpaceTable, + NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo); + RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors); + } + else { // universal tag selector + AppendUniversalRule(aRuleInfo); + RULE_HASH_STAT_INCREMENT(mUniversalSelectors); + } +} + +// this should cover practically all cases so we don't need to reallocate +#define MIN_ENUM_LIST_SIZE 8 + +#ifdef RULE_HASH_STATS +#define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \ + (var_) += (list_).Length() +#else +#define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \ + PR_BEGIN_MACRO PR_END_MACRO +#endif + +static inline +void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector, + ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext, + AncestorFilter *ancestorFilter); + +void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData, + NodeMatchContext& aNodeContext) +{ + int32_t nameSpace = aElement->GetNameSpaceID(); + nsIAtom* tag = aElement->NodeInfo()->NameAtom(); + nsIAtom* id = aElement->GetID(); + const nsAttrValue* classList = aElement->GetClasses(); + + MOZ_ASSERT(tag, "How could we not have a tag?"); + + int32_t classCount = classList ? classList->GetAtomCount() : 0; + + // assume 1 universal, tag, id, and namespace, rather than wasting + // time counting + int32_t testCount = classCount + 4; + + if (mEnumListSize < testCount) { + delete [] mEnumList; + mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE); + mEnumList = new EnumData[mEnumListSize]; + } + + int32_t valueCount = 0; + RULE_HASH_STAT_INCREMENT(mElementsMatched); + + if (mUniversalRules.Length() != 0) { // universal rules + mEnumList[valueCount++] = ToEnumData(mUniversalRules); + RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls); + } + // universal rules within the namespace + if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.EntryCount() > 0) { + auto entry = static_cast + (mNameSpaceTable.Search(NS_INT32_TO_PTR(nameSpace))); + if (entry) { + mEnumList[valueCount++] = ToEnumData(entry->mRules); + RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls); + } + } + if (mTagTable.EntryCount() > 0) { + auto entry = static_cast(mTagTable.Search(tag)); + if (entry) { + mEnumList[valueCount++] = ToEnumData(entry->mRules); + RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls); + } + } + if (id && mIdTable.EntryCount() > 0) { + auto entry = static_cast(mIdTable.Search(id)); + if (entry) { + mEnumList[valueCount++] = ToEnumData(entry->mRules); + RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls); + } + } + if (mClassTable.EntryCount() > 0) { + for (int32_t index = 0; index < classCount; ++index) { + auto entry = static_cast + (mClassTable.Search(classList->AtomAt(index))); + if (entry) { + mEnumList[valueCount++] = ToEnumData(entry->mRules); + RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls); + } + } + } + NS_ASSERTION(valueCount <= testCount, "values exceeded list size"); + + if (valueCount > 0) { + AncestorFilter *filter = + aData->mTreeMatchContext.mAncestorFilter.HasFilter() ? + &aData->mTreeMatchContext.mAncestorFilter : nullptr; +#ifdef DEBUG + if (filter) { + filter->AssertHasAllAncestors(aElement); + } +#endif + // Merge the lists while there are still multiple lists to merge. + while (valueCount > 1) { + int32_t valueIndex = 0; + int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex; + for (int32_t index = 1; index < valueCount; ++index) { + int32_t ruleIndex = mEnumList[index].mCurValue->mIndex; + if (ruleIndex < lowestRuleIndex) { + valueIndex = index; + lowestRuleIndex = ruleIndex; + } + } + const RuleValue *cur = mEnumList[valueIndex].mCurValue; + ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter); + cur++; + if (cur == mEnumList[valueIndex].mEnd) { + mEnumList[valueIndex] = mEnumList[--valueCount]; + } else { + mEnumList[valueIndex].mCurValue = cur; + } + } + + // Fast loop over single value. + for (const RuleValue *value = mEnumList[0].mCurValue, + *end = mEnumList[0].mEnd; + value != end; ++value) { + ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter); + } + } +} + +static size_t +SizeOfRuleHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf) +{ + size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { + auto entry = static_cast(iter.Get()); + n += entry->mRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + } + return n; +} + +size_t +RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + + n += SizeOfRuleHashTable(mIdTable, aMallocSizeOf); + + n += SizeOfRuleHashTable(mClassTable, aMallocSizeOf); + + n += SizeOfRuleHashTable(mTagTable, aMallocSizeOf); + + n += SizeOfRuleHashTable(mNameSpaceTable, aMallocSizeOf); + + n += mUniversalRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + + return n; +} + +size_t +RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + +//-------------------------------- + +/** + * A struct that stores an nsCSSSelector pointer along side a pointer to + * the rightmost nsCSSSelector in the selector. For example, for + * + * .main p > span + * + * if mSelector points to the |p| nsCSSSelector, mRightmostSelector would + * point to the |span| nsCSSSelector. + * + * Both mSelector and mRightmostSelector are always top-level selectors, + * i.e. they aren't selectors within a :not() or :-moz-any(). + */ +struct SelectorPair +{ + SelectorPair(nsCSSSelector* aSelector, nsCSSSelector* aRightmostSelector) + : mSelector(aSelector), mRightmostSelector(aRightmostSelector) + { + MOZ_ASSERT(aSelector); + MOZ_ASSERT(mRightmostSelector); + } + SelectorPair(const SelectorPair& aOther) = default; + nsCSSSelector* const mSelector; + nsCSSSelector* const mRightmostSelector; +}; + +// A hash table mapping atoms to lists of selectors +struct AtomSelectorEntry : public PLDHashEntryHdr { + nsIAtom *mAtom; + // Auto length 2, because a decent fraction of these arrays ends up + // with 2 elements, and each entry is cheap. + AutoTArray mSelectors; +}; + +static void +AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) +{ + (static_cast(hdr))->~AtomSelectorEntry(); +} + +static void +AtomSelector_InitEntry(PLDHashEntryHdr *hdr, const void *key) +{ + AtomSelectorEntry *entry = static_cast(hdr); + new (KnownNotNull, entry) AtomSelectorEntry(); + entry->mAtom = const_cast(static_cast(key)); +} + +static void +AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, + PLDHashEntryHdr *to) +{ + NS_PRECONDITION(from != to, "This is not going to work!"); + AtomSelectorEntry *oldEntry = + const_cast(static_cast(from)); + auto* newEntry = new (KnownNotNull, to) AtomSelectorEntry(); + newEntry->mAtom = oldEntry->mAtom; + newEntry->mSelectors.SwapElements(oldEntry->mSelectors); + oldEntry->~AtomSelectorEntry(); +} + +static bool +AtomSelector_CIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + const AtomSelectorEntry *entry = static_cast(hdr); + return CIMatchAtoms(key, entry->mAtom); +} + +// Case-sensitive ops. +static const PLDHashTableOps AtomSelector_CSOps = { + PLDHashTable::HashVoidPtrKeyStub, + PLDHashTable::MatchEntryStub, + AtomSelector_MoveEntry, + AtomSelector_ClearEntry, + AtomSelector_InitEntry +}; + +// Case-insensitive ops. +static const PLDHashTableOps AtomSelector_CIOps = { + RuleHash_CIHashKey, + AtomSelector_CIMatchEntry, + AtomSelector_MoveEntry, + AtomSelector_ClearEntry, + AtomSelector_InitEntry +}; + +//-------------------------------- + +struct RuleCascadeData { + RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode) + : mRuleHash(aQuirksMode), + mStateSelectors(), + mSelectorDocumentStates(0), + mClassSelectors(aQuirksMode ? &AtomSelector_CIOps + : &AtomSelector_CSOps, + sizeof(AtomSelectorEntry)), + mIdSelectors(aQuirksMode ? &AtomSelector_CIOps + : &AtomSelector_CSOps, + sizeof(AtomSelectorEntry)), + // mAttributeSelectors is matching on the attribute _name_, not the + // value, and we case-fold names at parse-time, so this is a + // case-sensitive match. + mAttributeSelectors(&AtomSelector_CSOps, sizeof(AtomSelectorEntry)), + mAnonBoxRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), +#ifdef MOZ_XUL + mXULTreeRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), +#endif + mKeyframesRuleTable(), + mCounterStyleRuleTable(), + mCacheKey(aMedium), + mNext(nullptr), + mQuirksMode(aQuirksMode) + { + memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes)); + } + + ~RuleCascadeData() + { + for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) { + delete mPseudoElementRuleHashes[i]; + } + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + RuleHash mRuleHash; + RuleHash* mPseudoElementRuleHashes[ + static_cast(CSSPseudoElementType::Count)]; + nsTArray mStateSelectors; + EventStates mSelectorDocumentStates; + PLDHashTable mClassSelectors; + PLDHashTable mIdSelectors; + nsTArray mPossiblyNegatedClassSelectors; + nsTArray mPossiblyNegatedIDSelectors; + PLDHashTable mAttributeSelectors; + PLDHashTable mAnonBoxRules; +#ifdef MOZ_XUL + PLDHashTable mXULTreeRules; +#endif + + nsTArray mFontFaceRules; + nsTArray mKeyframesRules; + nsTArray mFontFeatureValuesRules; + nsTArray mPageRules; + nsTArray mCounterStyleRules; + + nsDataHashtable mKeyframesRuleTable; + nsDataHashtable mCounterStyleRuleTable; + + // Looks up or creates the appropriate list in |mAttributeSelectors|. + // Returns null only on allocation failure. + nsTArray* AttributeListFor(nsIAtom* aAttribute); + + nsMediaQueryResultCacheKey mCacheKey; + RuleCascadeData* mNext; // for a different medium + + const bool mQuirksMode; +}; + +static size_t +SizeOfSelectorsHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf) +{ + size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { + auto entry = static_cast(iter.Get()); + n += entry->mSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); + } + return n; +} + +size_t +RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf); + for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) { + if (mPseudoElementRuleHashes[i]) + n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf); + } + + n += mStateSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += SizeOfSelectorsHashTable(mIdSelectors, aMallocSizeOf); + n += SizeOfSelectorsHashTable(mClassSelectors, aMallocSizeOf); + + n += mPossiblyNegatedClassSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mPossiblyNegatedIDSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += SizeOfSelectorsHashTable(mAttributeSelectors, aMallocSizeOf); + n += SizeOfRuleHashTable(mAnonBoxRules, aMallocSizeOf); +#ifdef MOZ_XUL + n += SizeOfRuleHashTable(mXULTreeRules, aMallocSizeOf); +#endif + + n += mFontFaceRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mKeyframesRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mFontFeatureValuesRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mPageRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mCounterStyleRules.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += mKeyframesRuleTable.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mKeyframesRuleTable.ConstIter(); !iter.Done(); iter.Next()) { + // We don't own the nsCSSKeyframesRule objects so we don't count them. We + // do care about the size of the keys' nsAString members' buffers though. + // + // Note that we depend on nsStringHashKey::GetKey() returning a reference, + // since otherwise aKey would be a copy of the string key and we would not + // be measuring the right object here. + n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } + + return n; +} + +nsTArray* +RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) +{ + auto entry = static_cast + (mAttributeSelectors.Add(aAttribute, fallible)); + if (!entry) + return nullptr; + return &entry->mSelectors; +} + +// ------------------------------- +// CSS Style rule processor implementation +// + +nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets, + SheetType aSheetType, + Element* aScopeElement, + nsCSSRuleProcessor* + aPreviousCSSRuleProcessor, + bool aIsShared) + : nsCSSRuleProcessor(sheet_array_type(aSheets), aSheetType, aScopeElement, + aPreviousCSSRuleProcessor, aIsShared) +{ +} + +nsCSSRuleProcessor::nsCSSRuleProcessor(sheet_array_type&& aSheets, + SheetType aSheetType, + Element* aScopeElement, + nsCSSRuleProcessor* + aPreviousCSSRuleProcessor, + bool aIsShared) + : mSheets(aSheets) + , mRuleCascades(nullptr) + , mPreviousCacheKey(aPreviousCSSRuleProcessor + ? aPreviousCSSRuleProcessor->CloneMQCacheKey() + : UniquePtr()) + , mLastPresContext(nullptr) + , mScopeElement(aScopeElement) + , mStyleSetRefCnt(0) + , mSheetType(aSheetType) + , mIsShared(aIsShared) + , mMustGatherDocumentRules(aIsShared) + , mInRuleProcessorCache(false) +#ifdef DEBUG + , mDocumentRulesAndCacheKeyValid(false) +#endif +{ + NS_ASSERTION(!!mScopeElement == (aSheetType == SheetType::ScopedDoc), + "aScopeElement must be specified iff aSheetType is " + "eScopedDocSheet"); + for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) { + mSheets[i]->AddRuleProcessor(this); + } +} + +nsCSSRuleProcessor::~nsCSSRuleProcessor() +{ + if (mInRuleProcessorCache) { + RuleProcessorCache::RemoveRuleProcessor(this); + } + MOZ_ASSERT(!mExpirationState.IsTracked()); + MOZ_ASSERT(mStyleSetRefCnt == 0); + ClearSheets(); + ClearRuleCascades(); +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSRuleProcessor) + NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSRuleProcessor) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSRuleProcessor) + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSRuleProcessor) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSRuleProcessor) + tmp->ClearSheets(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mScopeElement) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSRuleProcessor) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +void +nsCSSRuleProcessor::ClearSheets() +{ + for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) { + mSheets[i]->DropRuleProcessor(this); + } + mSheets.Clear(); +} + +/* static */ void +nsCSSRuleProcessor::Startup() +{ + Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF, + true); +} + +static bool +InitSystemMetrics() +{ + NS_ASSERTION(!sSystemMetrics, "already initialized"); + + sSystemMetrics = new nsTArray< nsCOMPtr >; + NS_ENSURE_TRUE(sSystemMetrics, false); + + /*************************************************************************** + * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN * + * nsMediaFeatures.cpp * + ***************************************************************************/ + + int32_t metricResult = + LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle); + if (metricResult & LookAndFeel::eScrollArrow_StartBackward) { + sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward); + } + if (metricResult & LookAndFeel::eScrollArrow_StartForward) { + sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward); + } + if (metricResult & LookAndFeel::eScrollArrow_EndBackward) { + sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward); + } + if (metricResult & LookAndFeel::eScrollArrow_EndForward) { + sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward); + } + + metricResult = + LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle); + if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) { + sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional); + } + + metricResult = + LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars); + if (metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars); + } + + metricResult = + LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag); + if (metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag); + } + + nsresult rv = + LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacYosemiteTheme, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::mac_yosemite_theme); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::windows_glass); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::windows_classic); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled, + &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled); + } + + rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton, + &metricResult); + if (NS_SUCCEEDED(rv) && metricResult) { + sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button); + } + +#ifdef XP_WIN + if (NS_SUCCEEDED( + LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier, + &metricResult))) { + nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast(metricResult)); + switch(metricResult) { + case LookAndFeel::eWindowsTheme_Aero: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero); + break; + case LookAndFeel::eWindowsTheme_AeroLite: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite); + break; + case LookAndFeel::eWindowsTheme_LunaBlue: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue); + break; + case LookAndFeel::eWindowsTheme_LunaOlive: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive); + break; + case LookAndFeel::eWindowsTheme_LunaSilver: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver); + break; + case LookAndFeel::eWindowsTheme_Royale: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale); + break; + case LookAndFeel::eWindowsTheme_Zune: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune); + break; + case LookAndFeel::eWindowsTheme_Generic: + sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic); + break; + } + } +#endif + + return true; +} + +/* static */ void +nsCSSRuleProcessor::FreeSystemMetrics() +{ + delete sSystemMetrics; + sSystemMetrics = nullptr; +} + +/* static */ void +nsCSSRuleProcessor::Shutdown() +{ + FreeSystemMetrics(); +} + +/* static */ bool +nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric) +{ + if (!sSystemMetrics && !InitSystemMetrics()) { + return false; + } + return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex; +} + +#ifdef XP_WIN +/* static */ uint8_t +nsCSSRuleProcessor::GetWindowsThemeIdentifier() +{ + if (!sSystemMetrics) + InitSystemMetrics(); + return sWinThemeId; +} +#endif + +/* static */ +EventStates +nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext) +{ + EventStates state = aElement->StyleState(); + + // If we are not supposed to mark visited links as such, be sure to + // flip the bits appropriately. We want to do this here, rather + // than in GetContentStateForVisitedHandling, so that we don't + // expose that :visited support is disabled to the Web page. + if (state.HasState(NS_EVENT_STATE_VISITED) && + (!gSupportVisitedPseudo || + aElement->OwnerDoc()->IsBeingUsedAsImage() || + aTreeMatchContext.mUsingPrivateBrowsing)) { + state &= ~NS_EVENT_STATE_VISITED; + state |= NS_EVENT_STATE_UNVISITED; + } + return state; +} + +/* static */ +bool +nsCSSRuleProcessor::IsLink(const Element* aElement) +{ + EventStates state = aElement->StyleState(); + return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED); +} + +/* static */ +EventStates +nsCSSRuleProcessor::GetContentStateForVisitedHandling( + Element* aElement, + const TreeMatchContext& aTreeMatchContext, + nsRuleWalker::VisitedHandlingType aVisitedHandling, + bool aIsRelevantLink) +{ + EventStates contentState = GetContentState(aElement, aTreeMatchContext); + if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) { + MOZ_ASSERT(IsLink(aElement), "IsLink() should match state"); + contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED); + if (aIsRelevantLink) { + switch (aVisitedHandling) { + case nsRuleWalker::eRelevantLinkUnvisited: + contentState |= NS_EVENT_STATE_UNVISITED; + break; + case nsRuleWalker::eRelevantLinkVisited: + contentState |= NS_EVENT_STATE_VISITED; + break; + case nsRuleWalker::eLinksVisitedOrUnvisited: + contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED; + break; + } + } else { + contentState |= NS_EVENT_STATE_UNVISITED; + } + } + return contentState; +} + +/** + * A |NodeMatchContext| has data about matching a selector (without + * combinators) against a single node. It contains only input to the + * matching. + * + * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext| + * can vary depending on the selector matching process. In other words, + * there might be multiple NodeMatchContexts corresponding to a single + * node, but only one possible RuleProcessorData. + */ +struct NodeMatchContext { + // In order to implement nsCSSRuleProcessor::HasStateDependentStyle, + // we need to be able to see if a node might match an + // event-state-dependent selector for any value of that event state. + // So mStateMask contains the states that should NOT be tested. + // + // NOTE: For |mStateMask| to work correctly, it's important that any + // change that changes multiple state bits include all those state + // bits in the notification. Otherwise, if multiple states change but + // we do separate notifications then we might determine the style is + // not state-dependent when it really is (e.g., determining that a + // :hover:active rule no longer matches when both states are unset). + const EventStates mStateMask; + + // Is this link the unique link whose visitedness can affect the style + // of the node being matched? (That link is the nearest link to the + // node being matched that is itself or an ancestor.) + // + // Always false when TreeMatchContext::mForStyling is false. (We + // could figure it out for SelectorListMatches, but we're starting + // from the middle of the selector list when doing + // Has{Attribute,State}DependentStyle, so we can't tell. So when + // mForStyling is false, we have to assume we don't know.) + const bool mIsRelevantLink; + + NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink) + : mStateMask(aStateMask) + , mIsRelevantLink(aIsRelevantLink) + { + } +}; + +/** + * Additional information about a selector (without combinators) that is + * being matched. + */ +enum class SelectorMatchesFlags : uint8_t { + NONE = 0, + + // The selector's flags are unknown. This happens when you don't know + // if you're starting from the top of a selector. Only used in cases + // where it's acceptable for matching to return a false positive. + // (It's not OK to return a false negative.) + UNKNOWN = 1 << 0, + + // The selector is part of a compound selector which has been split in + // half, where the other half is a pseudo-element. The current + // selector is not a pseudo-element itself. + HAS_PSEUDO_ELEMENT = 1 << 1, + + // The selector is part of an argument to a functional pseudo-class or + // pseudo-element. + IS_PSEUDO_CLASS_ARGUMENT = 1 << 2 +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags) + +// Return whether the selector matches conditions for the :active and +// :hover quirk. +static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector, + SelectorMatchesFlags aSelectorFlags) +{ + if (aSelector->HasTagSelector() || aSelector->mAttrList || + aSelector->mIDList || aSelector->mClassList || + aSelector->IsPseudoElement() || + // Having this quirk means that some selectors will no longer match, + // so it's better to return false when we aren't sure (i.e., the + // flags are unknown). + aSelectorFlags & (SelectorMatchesFlags::UNKNOWN | + SelectorMatchesFlags::HAS_PSEUDO_ELEMENT | + SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { + return false; + } + + // No pseudo-class other than :active and :hover. + for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + if (pseudoClass->mType != CSSPseudoClassType::hover && + pseudoClass->mType != CSSPseudoClassType::active) { + return false; + } + } + + return true; +} + + +static inline bool +IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant, + bool aWhitespaceIsSignificant) +{ + return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant, + aWhitespaceIsSignificant); +} + +// This function is to be called once we have fetched a value for an attribute +// whose namespace and name match those of aAttrSelector. This function +// performs comparisons on the value only, based on aAttrSelector->mFunction. +static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector, + const nsString& aValue, bool isHTML) +{ + NS_PRECONDITION(aAttrSelector, "Must have an attribute selector"); + + // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html + // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH) + // all accept the empty string, but match nothing. + if (aAttrSelector->mValue.IsEmpty() && + (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES || + aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH || + aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH || + aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)) + return false; + + const nsDefaultStringComparator defaultComparator; + const nsASCIICaseInsensitiveStringComparator ciComparator; + const nsStringComparator& comparator = + aAttrSelector->IsValueCaseSensitive(isHTML) + ? static_cast(defaultComparator) + : static_cast(ciComparator); + + switch (aAttrSelector->mFunction) { + case NS_ATTR_FUNC_EQUALS: + return aValue.Equals(aAttrSelector->mValue, comparator); + case NS_ATTR_FUNC_INCLUDES: + return nsStyleUtil::ValueIncludes(aValue, aAttrSelector->mValue, comparator); + case NS_ATTR_FUNC_DASHMATCH: + return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator); + case NS_ATTR_FUNC_ENDSMATCH: + return StringEndsWith(aValue, aAttrSelector->mValue, comparator); + case NS_ATTR_FUNC_BEGINSMATCH: + return StringBeginsWith(aValue, aAttrSelector->mValue, comparator); + case NS_ATTR_FUNC_CONTAINSMATCH: + return FindInReadable(aAttrSelector->mValue, aValue, comparator); + default: + NS_NOTREACHED("Shouldn't be ending up here"); + return false; + } +} + +static inline bool +edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext, + bool checkFirst, bool checkLast) +{ + nsIContent* parent = aElement->GetParent(); + if (parent && aTreeMatchContext.mForStyling) + parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); + + return (!checkFirst || + aTreeMatchContext.mNthIndexCache. + GetNthIndex(aElement, false, false, true) == 1) && + (!checkLast || + aTreeMatchContext.mNthIndexCache. + GetNthIndex(aElement, false, true, true) == 1); +} + +static inline bool +nthChildGenericMatches(Element* aElement, + TreeMatchContext& aTreeMatchContext, + nsPseudoClassList* pseudoClass, + bool isOfType, bool isFromEnd) +{ + nsIContent* parent = aElement->GetParent(); + if (parent && aTreeMatchContext.mForStyling) { + if (isFromEnd) + parent->SetFlags(NODE_HAS_SLOW_SELECTOR); + else + parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); + } + + const int32_t index = aTreeMatchContext.mNthIndexCache. + GetNthIndex(aElement, isOfType, isFromEnd, false); + if (index <= 0) { + // Node is anonymous content (not really a child of its parent). + return false; + } + + const int32_t a = pseudoClass->u.mNumbers[0]; + const int32_t b = pseudoClass->u.mNumbers[1]; + // result should be true if there exists n >= 0 such that + // a * n + b == index. + if (a == 0) { + return b == index; + } + + // Integer division in C does truncation (towards 0). So + // check that the result is nonnegative, and that there was no + // truncation. + const CheckedInt indexMinusB = CheckedInt(index) - b; + const CheckedInt n = indexMinusB / a; + return n.isValid() && + n.value() >= 0 && + a * n == indexMinusB; +} + +static inline bool +edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext, + bool checkFirst, bool checkLast) +{ + nsIContent *parent = aElement->GetParent(); + if (parent && aTreeMatchContext.mForStyling) { + if (checkLast) + parent->SetFlags(NODE_HAS_SLOW_SELECTOR); + else + parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); + } + + return (!checkFirst || + aTreeMatchContext.mNthIndexCache. + GetNthIndex(aElement, true, false, true) == 1) && + (!checkLast || + aTreeMatchContext.mNthIndexCache. + GetNthIndex(aElement, true, true, true) == 1); +} + +static inline bool +checkGenericEmptyMatches(Element* aElement, + TreeMatchContext& aTreeMatchContext, + bool isWhitespaceSignificant) +{ + nsIContent *child = nullptr; + int32_t index = -1; + + if (aTreeMatchContext.mForStyling) + aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR); + + do { + child = aElement->GetChildAt(++index); + // stop at first non-comment (and non-whitespace for + // :-moz-only-whitespace) node + } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant)); + return (child == nullptr); +} + +// Arrays of the states that are relevant for various pseudoclasses. +static const EventStates sPseudoClassStateDependences[] = { +#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ + EventStates(), +#define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \ + _states, +#include "nsCSSPseudoClassList.h" +#undef CSS_STATE_DEPENDENT_PSEUDO_CLASS +#undef CSS_PSEUDO_CLASS + // Add more entries for our fake values to make sure we can't + // index out of bounds into this array no matter what. + EventStates(), + EventStates() +}; + +static const EventStates sPseudoClassStates[] = { +#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ + EventStates(), +#define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \ + _states, +#include "nsCSSPseudoClassList.h" +#undef CSS_STATE_PSEUDO_CLASS +#undef CSS_PSEUDO_CLASS + // Add more entries for our fake values to make sure we can't + // index out of bounds into this array no matter what. + EventStates(), + EventStates() +}; +static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) == + static_cast(CSSPseudoClassType::MAX), + "CSSPseudoClassType::MAX is no longer equal to the length of " + "sPseudoClassStates"); + +static bool +StateSelectorMatches(Element* aElement, + nsCSSSelector* aSelector, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesFlags aSelectorFlags, + bool* const aDependence, + EventStates aStatesToCheck) +{ + NS_PRECONDITION(!aStatesToCheck.IsEmpty(), + "should only need to call StateSelectorMatches if " + "aStatesToCheck is not empty"); + + // Bit-based pseudo-classes + if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | + NS_EVENT_STATE_HOVER) && + aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks && + ActiveHoverQuirkMatches(aSelector, aSelectorFlags) && + aElement->IsHTMLElement() && !nsCSSRuleProcessor::IsLink(aElement)) { + // In quirks mode, only make links sensitive to selectors ":active" + // and ":hover". + return false; + } + + if (aTreeMatchContext.mForStyling && + aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) { + // Mark the element as having :hover-dependent style + aElement->SetHasRelevantHoverRules(); + } + + if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) { + if (aDependence) { + *aDependence = true; + } + } else { + EventStates contentState = + nsCSSRuleProcessor::GetContentStateForVisitedHandling( + aElement, + aTreeMatchContext, + aTreeMatchContext.VisitedHandling(), + aNodeMatchContext.mIsRelevantLink); + if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) { + return false; + } + } + + return true; +} + +static bool +StateSelectorMatches(Element* aElement, + nsCSSSelector* aSelector, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesFlags aSelectorFlags) +{ + for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + auto idx = static_cast(pseudoClass->mType); + EventStates statesToCheck = sPseudoClassStates[idx]; + if (!statesToCheck.IsEmpty() && + !StateSelectorMatches(aElement, aSelector, aNodeMatchContext, + aTreeMatchContext, aSelectorFlags, nullptr, + statesToCheck)) { + return false; + } + } + return true; +} + +// |aDependence| has two functions: +// * when non-null, it indicates that we're processing a negation, +// which is done only when SelectorMatches calls itself recursively +// * what it points to should be set to true whenever a test is skipped +// because of aNodeMatchContent.mStateMask +static bool SelectorMatches(Element* aElement, + nsCSSSelector* aSelector, + NodeMatchContext& aNodeMatchContext, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesFlags aSelectorFlags, + bool* const aDependence = nullptr) +{ + NS_PRECONDITION(!aSelector->IsPseudoElement(), + "Pseudo-element snuck into SelectorMatches?"); + MOZ_ASSERT(aTreeMatchContext.mForStyling || + !aNodeMatchContext.mIsRelevantLink, + "mIsRelevantLink should be set to false when mForStyling " + "is false since we don't know how to set it correctly in " + "Has(Attribute|State)DependentStyle"); + + // namespace/tag match + // optimization : bail out early if we can + if ((kNameSpaceID_Unknown != aSelector->mNameSpace && + aElement->GetNameSpaceID() != aSelector->mNameSpace)) + return false; + + if (aSelector->mLowercaseTag) { + nsIAtom* selectorTag = + (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()) ? + aSelector->mLowercaseTag : aSelector->mCasedTag; + if (selectorTag != aElement->NodeInfo()->NameAtom()) { + return false; + } + } + + nsAtomList* IDList = aSelector->mIDList; + if (IDList) { + nsIAtom* id = aElement->GetID(); + if (id) { + // case sensitivity: bug 93371 + const bool isCaseSensitive = + aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks; + + if (isCaseSensitive) { + do { + if (IDList->mAtom != id) { + return false; + } + IDList = IDList->mNext; + } while (IDList); + } else { + // Use EqualsIgnoreASCIICase instead of full on unicode case conversion + // in order to save on performance. This is only used in quirks mode + // anyway. + nsDependentAtomString id1Str(id); + do { + if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str, + nsDependentAtomString(IDList->mAtom))) { + return false; + } + IDList = IDList->mNext; + } while (IDList); + } + } else { + // Element has no id but we have an id selector + return false; + } + } + + nsAtomList* classList = aSelector->mClassList; + if (classList) { + // test for class match + const nsAttrValue *elementClasses = aElement->GetClasses(); + if (!elementClasses) { + // Element has no classes but we have a class selector + return false; + } + + // case sensitivity: bug 93371 + const bool isCaseSensitive = + aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks; + + while (classList) { + if (!elementClasses->Contains(classList->mAtom, + isCaseSensitive ? + eCaseMatters : eIgnoreCase)) { + return false; + } + classList = classList->mNext; + } + } + + const bool isNegated = (aDependence != nullptr); + // The selectors for which we set node bits are, unfortunately, early + // in this function (because they're pseudo-classes, which are + // generally quick to test, and thus earlier). If they were later, + // we'd probably avoid setting those bits in more cases where setting + // them is unnecessary. + NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() || + !aTreeMatchContext.mForStyling, + "mForStyling must be false if we're just testing for " + "state-dependence"); + + // test for pseudo class match + for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + auto idx = static_cast(pseudoClass->mType); + EventStates statesToCheck = sPseudoClassStates[idx]; + if (statesToCheck.IsEmpty()) { + // keep the cases here in the same order as the list in + // nsCSSPseudoClassList.h + switch (pseudoClass->mType) { + case CSSPseudoClassType::empty: + if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) { + return false; + } + break; + + case CSSPseudoClassType::mozOnlyWhitespace: + if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) { + return false; + } + break; + + case CSSPseudoClassType::mozEmptyExceptChildrenWithLocalname: + { + NS_ASSERTION(pseudoClass->u.mString, "Must have string!"); + nsIContent *child = nullptr; + int32_t index = -1; + + if (aTreeMatchContext.mForStyling) + // FIXME: This isn't sufficient to handle: + // :-moz-empty-except-children-with-localname() + E + // :-moz-empty-except-children-with-localname() ~ E + // because we don't know to restyle the grandparent of the + // inserted/removed element (as in bug 534804 for :empty). + aElement->SetFlags(NODE_HAS_SLOW_SELECTOR); + do { + child = aElement->GetChildAt(++index); + } while (child && + (!IsSignificantChild(child, true, false) || + (child->GetNameSpaceID() == aElement->GetNameSpaceID() && + child->NodeInfo()->NameAtom()->Equals(nsDependentString(pseudoClass->u.mString))))); + if (child != nullptr) { + return false; + } + } + break; + + case CSSPseudoClassType::lang: + { + NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter"); + if (!pseudoClass->u.mString || !*pseudoClass->u.mString) { + 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. + nsAutoString language; + if (aElement->GetLang(language)) { + if (!nsStyleUtil::DashMatchCompare(language, + nsDependentString(pseudoClass->u.mString), + nsASCIICaseInsensitiveStringComparator())) { + return false; + } + // This pseudo-class matched; move on to the next thing + break; + } + + nsIDocument* doc = aTreeMatchContext.mDocument; + if (doc) { + // 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. + doc->GetContentLanguage(language); + + nsDependentString langString(pseudoClass->u.mString); + language.StripWhitespace(); + int32_t begin = 0; + int32_t len = language.Length(); + while (begin < len) { + int32_t end = language.FindChar(char16_t(','), begin); + if (end == kNotFound) { + end = len; + } + if (nsStyleUtil::DashMatchCompare(Substring(language, begin, + end-begin), + langString, + nsASCIICaseInsensitiveStringComparator())) { + break; + } + begin = end + 1; + } + if (begin < len) { + // This pseudo-class matched + break; + } + } + } + return false; + + case CSSPseudoClassType::mozBoundElement: + if (aTreeMatchContext.mScopedRoot != aElement) { + return false; + } + break; + + case CSSPseudoClassType::root: + if (aElement != aElement->OwnerDoc()->GetRootElement()) { + return false; + } + break; + + case CSSPseudoClassType::any: + { + nsCSSSelectorList *l; + for (l = pseudoClass->u.mSelectors; l; l = l->mNext) { + nsCSSSelector *s = l->mSelectors; + MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(), + "parser failed"); + if (SelectorMatches( + aElement, s, aNodeMatchContext, aTreeMatchContext, + SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { + break; + } + } + if (!l) { + return false; + } + } + break; + + case CSSPseudoClassType::firstChild: + if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) { + return false; + } + break; + + case CSSPseudoClassType::firstNode: + { + nsIContent *firstNode = nullptr; + nsIContent *parent = aElement->GetParent(); + if (parent) { + if (aTreeMatchContext.mForStyling) + parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); + + int32_t index = -1; + do { + firstNode = parent->GetChildAt(++index); + // stop at first non-comment and non-whitespace node + } while (firstNode && + !IsSignificantChild(firstNode, true, false)); + } + if (aElement != firstNode) { + return false; + } + } + break; + + case CSSPseudoClassType::lastChild: + if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) { + return false; + } + break; + + case CSSPseudoClassType::lastNode: + { + nsIContent *lastNode = nullptr; + nsIContent *parent = aElement->GetParent(); + if (parent) { + if (aTreeMatchContext.mForStyling) + parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); + + uint32_t index = parent->GetChildCount(); + do { + lastNode = parent->GetChildAt(--index); + // stop at first non-comment and non-whitespace node + } while (lastNode && + !IsSignificantChild(lastNode, true, false)); + } + if (aElement != lastNode) { + return false; + } + } + break; + + case CSSPseudoClassType::onlyChild: + if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) { + return false; + } + break; + + case CSSPseudoClassType::firstOfType: + if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) { + return false; + } + break; + + case CSSPseudoClassType::lastOfType: + if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) { + return false; + } + break; + + case CSSPseudoClassType::onlyOfType: + if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) { + return false; + } + break; + + case CSSPseudoClassType::nthChild: + if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, + false, false)) { + return false; + } + break; + + case CSSPseudoClassType::nthLastChild: + if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, + false, true)) { + return false; + } + break; + + case CSSPseudoClassType::nthOfType: + if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, + true, false)) { + return false; + } + break; + + case CSSPseudoClassType::nthLastOfType: + if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, + true, true)) { + return false; + } + break; + + case CSSPseudoClassType::mozIsHTML: + if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTMLElement()) { + return false; + } + break; + + case CSSPseudoClassType::mozNativeAnonymous: + if (!aElement->IsInNativeAnonymousSubtree()) { + return false; + } + break; + + case CSSPseudoClassType::mozSystemMetric: + { + nsCOMPtr metric = NS_Atomize(pseudoClass->u.mString); + if (!nsCSSRuleProcessor::HasSystemMetric(metric)) { + return false; + } + } + break; + + case CSSPseudoClassType::mozLocaleDir: + { + bool docIsRTL = + aTreeMatchContext.mDocument->GetDocumentState(). + HasState(NS_DOCUMENT_STATE_RTL_LOCALE); + + nsDependentString dirString(pseudoClass->u.mString); + + if (dirString.EqualsLiteral("rtl")) { + if (!docIsRTL) { + return false; + } + } else if (dirString.EqualsLiteral("ltr")) { + if (docIsRTL) { + return false; + } + } else { + // Selectors specifying other directions never match. + return false; + } + } + break; + + case CSSPseudoClassType::mozLWTheme: + { + if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <= + nsIDocument::Doc_Theme_None) { + return false; + } + } + break; + + case CSSPseudoClassType::mozLWThemeBrightText: + { + if (aTreeMatchContext.mDocument->GetDocumentLWTheme() != + nsIDocument::Doc_Theme_Bright) { + return false; + } + } + break; + + case CSSPseudoClassType::mozLWThemeDarkText: + { + if (aTreeMatchContext.mDocument->GetDocumentLWTheme() != + nsIDocument::Doc_Theme_Dark) { + return false; + } + } + break; + + case CSSPseudoClassType::mozWindowInactive: + if (!aTreeMatchContext.mDocument->GetDocumentState(). + HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { + return false; + } + break; + + case CSSPseudoClassType::mozTableBorderNonzero: + { + if (!aElement->IsHTMLElement(nsGkAtoms::table)) { + return false; + } + const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border); + if (!val || + (val->Type() == nsAttrValue::eInteger && + val->GetIntegerValue() == 0)) { + return false; + } + } + break; + + case CSSPseudoClassType::mozBrowserFrame: + { + nsCOMPtr + browserFrame = do_QueryInterface(aElement); + if (!browserFrame || + !browserFrame->GetReallyIsBrowserOrApp()) { + return false; + } + } + break; + + case CSSPseudoClassType::mozDir: + case CSSPseudoClassType::dir: + { + if (aDependence) { + EventStates states = sPseudoClassStateDependences[ + static_cast(pseudoClass->mType)]; + if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) { + *aDependence = true; + return false; + } + } + + // If we only had to consider HTML, directionality would be + // exclusively LTR or RTL. + // + // However, in markup languages where there is no direction attribute + // we have to consider the possibility that neither dir(rtl) nor + // dir(ltr) matches. + EventStates state = aElement->StyleState(); + nsDependentString dirString(pseudoClass->u.mString); + + if (dirString.EqualsLiteral("rtl")) { + if (!state.HasState(NS_EVENT_STATE_RTL)) { + return false; + } + } else if (dirString.EqualsLiteral("ltr")) { + if (!state.HasState(NS_EVENT_STATE_LTR)) { + return false; + } + } else { + // Selectors specifying other directions never match. + return false; + } + } + break; + + case CSSPseudoClassType::scope: + if (aTreeMatchContext.mForScopedStyle) { + if (aTreeMatchContext.mCurrentStyleScope) { + // If mCurrentStyleScope is null, aElement must be the style + // scope root. This is because the PopStyleScopeForSelectorMatching + // call in SelectorMatchesTree sets mCurrentStyleScope to null + // as soon as we visit the style scope element, and we won't + // progress further up the tree after this call to + // SelectorMatches. Thus if mCurrentStyleScope is still set, + // we know the selector does not match. + return false; + } + } else if (aTreeMatchContext.HasSpecifiedScope()) { + if (!aTreeMatchContext.IsScopeElement(aElement)) { + return false; + } + } else { + if (aElement != aElement->OwnerDoc()->GetRootElement()) { + return false; + } + } + break; + + default: + MOZ_ASSERT(false, "How did that happen?"); + } + } else { + if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext, + aTreeMatchContext, aSelectorFlags, aDependence, + statesToCheck)) { + return false; + } + } + } + + bool result = true; + if (aSelector->mAttrList) { + // test for attribute match + if (!aElement->HasAttrs()) { + // if no attributes on the content, no match + return false; + } else { + result = true; + nsAttrSelector* attr = aSelector->mAttrList; + nsIAtom* matchAttribute; + + do { + bool isHTML = + (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()); + matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr; + if (attr->mNameSpace == kNameSpaceID_Unknown) { + // Attr selector with a wildcard namespace. We have to examine all + // the attributes on our content node.... This sort of selector is + // essentially a boolean OR, over all namespaces, of equivalent attr + // selectors with those namespaces. So to evaluate whether it + // matches, evaluate for each namespace (the only namespaces that + // have a chance at matching, of course, are ones that the element + // actually has attributes in), short-circuiting if we ever match. + result = false; + const nsAttrName* attrName; + for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) { + if (attrName->LocalName() != matchAttribute) { + continue; + } + if (attr->mFunction == NS_ATTR_FUNC_SET) { + result = true; + } else { + nsAutoString value; +#ifdef DEBUG + bool hasAttr = +#endif + aElement->GetAttr(attrName->NamespaceID(), + attrName->LocalName(), value); + NS_ASSERTION(hasAttr, "GetAttrNameAt lied"); + result = AttrMatchesValue(attr, value, isHTML); + } + + // At this point |result| has been set by us + // explicitly in this loop. If it's false, we may still match + // -- the content may have another attribute with the same name but + // in a different namespace. But if it's true, we are done (we + // can short-circuit the boolean OR described above). + if (result) { + break; + } + } + } + else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) { + result = + aElement-> + AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue, + attr->IsValueCaseSensitive(isHTML) ? eCaseMatters + : eIgnoreCase); + } + else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) { + result = false; + } + else if (attr->mFunction != NS_ATTR_FUNC_SET) { + nsAutoString value; +#ifdef DEBUG + bool hasAttr = +#endif + aElement->GetAttr(attr->mNameSpace, matchAttribute, value); + NS_ASSERTION(hasAttr, "HasAttr lied"); + result = AttrMatchesValue(attr, value, isHTML); + } + + attr = attr->mNext; + } while (attr && result); + } + } + + // apply SelectorMatches to the negated selectors in the chain + if (!isNegated) { + for (nsCSSSelector *negation = aSelector->mNegations; + result && negation; negation = negation->mNegations) { + bool dependence = false; + result = !SelectorMatches(aElement, negation, aNodeMatchContext, + aTreeMatchContext, + SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT, + &dependence); + // If the selector does match due to the dependence on + // aNodeMatchContext.mStateMask, then we want to keep result true + // so that the final result of SelectorMatches is true. Doing so + // tells StateEnumFunc that there is a dependence on the state. + result = result || dependence; + } + } + return result; +} + +#undef STATE_CHECK + +#ifdef DEBUG +static bool +HasPseudoClassSelectorArgsWithCombinators(nsCSSSelector* aSelector) +{ + for (nsPseudoClassList* p = aSelector->mPseudoClassList; p; p = p->mNext) { + if (nsCSSPseudoClasses::HasSelectorListArg(p->mType)) { + for (nsCSSSelectorList* l = p->u.mSelectors; l; l = l->mNext) { + if (l->mSelectors->mNext) { + return true; + } + } + } + } + for (nsCSSSelector* n = aSelector->mNegations; n; n = n->mNegations) { + if (n->mNext) { + return true; + } + } + return false; +} +#endif + +/* static */ bool +nsCSSRuleProcessor::RestrictedSelectorMatches( + Element* aElement, + nsCSSSelector* aSelector, + TreeMatchContext& aTreeMatchContext) +{ + MOZ_ASSERT(aSelector->IsRestrictedSelector(), + "aSelector must not have a pseudo-element"); + + NS_WARNING_ASSERTION( + !HasPseudoClassSelectorArgsWithCombinators(aSelector), + "processing eRestyle_SomeDescendants can be slow if pseudo-classes with " + "selector arguments can now have combinators in them"); + + // We match aSelector as if :visited and :link both match visited and + // unvisited links. + + NodeMatchContext nodeContext(EventStates(), + nsCSSRuleProcessor::IsLink(aElement)); + if (nodeContext.mIsRelevantLink) { + aTreeMatchContext.SetHaveRelevantLink(); + } + aTreeMatchContext.ResetForUnvisitedMatching(); + bool matches = SelectorMatches(aElement, aSelector, nodeContext, + aTreeMatchContext, SelectorMatchesFlags::NONE); + if (nodeContext.mIsRelevantLink) { + aTreeMatchContext.ResetForVisitedMatching(); + if (SelectorMatches(aElement, aSelector, nodeContext, aTreeMatchContext, + SelectorMatchesFlags::NONE)) { + matches = true; + } + } + return matches; +} + +// Right now, there are four operators: +// ' ', the descendant combinator, is greedy +// '~', the indirect adjacent sibling combinator, is greedy +// '+' and '>', the direct adjacent sibling and child combinators, are not +#define NS_IS_GREEDY_OPERATOR(ch) \ + ((ch) == char16_t(' ') || (ch) == char16_t('~')) + +/** + * Flags for SelectorMatchesTree. + */ +enum SelectorMatchesTreeFlags { + // Whether we still have not found the closest ancestor link element and + // thus have to check the current element for it. + eLookForRelevantLink = 0x1, + + // Whether SelectorMatchesTree should check for, and return true upon + // finding, an ancestor element that has an eRestyle_SomeDescendants + // restyle hint pending. + eMatchOnConditionalRestyleAncestor = 0x2, +}; + +static bool +SelectorMatchesTree(Element* aPrevElement, + nsCSSSelector* aSelector, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesTreeFlags aFlags) +{ + MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement()); + nsCSSSelector* selector = aSelector; + Element* prevElement = aPrevElement; + while (selector) { // check compound selectors + NS_ASSERTION(!selector->mNext || + selector->mNext->mOperator != char16_t(0), + "compound selector without combinator"); + + // If after the previous selector match we are now outside the + // current style scope, we don't need to match any further. + if (aTreeMatchContext.mForScopedStyle && + !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) { + return false; + } + + // for adjacent sibling combinators, the content to test against the + // selector is the previous sibling *element* + Element* element = nullptr; + if (char16_t('+') == selector->mOperator || + char16_t('~') == selector->mOperator) { + // The relevant link must be an ancestor of the node being matched. + aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); + nsIContent* parent = prevElement->GetParent(); + if (parent) { + if (aTreeMatchContext.mForStyling) + parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); + + element = prevElement->GetPreviousElementSibling(); + } + } + // for descendant combinators and child combinators, the element + // to test against is the parent + else { + nsIContent *content = prevElement->GetParent(); + // GetParent could return a document fragment; we only want + // element parents. + if (content && content->IsElement()) { + element = content->AsElement(); + if (aTreeMatchContext.mForScopedStyle) { + // We are moving up to the parent element; tell the + // TreeMatchContext, so that in case this element is the + // style scope element, selector matching stops before we + // traverse further up the tree. + aTreeMatchContext.PopStyleScopeForSelectorMatching(element); + } + + // Compatibility hack: First try matching this selector as though the + // element wasn't in the tree to allow old selectors + // were written before participated in CSS selector + // matching to work. + if (selector->mOperator == '>' && element->IsActiveChildrenElement()) { + Element* styleScope = aTreeMatchContext.mCurrentStyleScope; + if (SelectorMatchesTree(element, selector, aTreeMatchContext, + aFlags)) { + // It matched, don't try matching on the element at + // all. + return true; + } + // We want to reset mCurrentStyleScope on aTreeMatchContext + // back to its state before the SelectorMatchesTree call, in + // case that call happens to traverse past the style scope element + // and sets it to null. + aTreeMatchContext.mCurrentStyleScope = styleScope; + } + } + } + if (!element) { + return false; + } + if ((aFlags & eMatchOnConditionalRestyleAncestor) && + element->HasFlag(ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)) { + // If we're looking at an element that we already generated an + // eRestyle_SomeDescendants restyle hint for, then we should pretend + // that we matched here, because we don't know what the values of + // attributes on |element| were at the time we generated the + // eRestyle_SomeDescendants. This causes AttributeEnumFunc and + // HasStateDependentStyle below to generate a restyle hint for the + // change we're currently looking at, as we don't know whether the LHS + // of the selector we looked up matches or not. (We only pass in aFlags + // to cause us to look for eRestyle_SomeDescendants here under + // AttributeEnumFunc and HasStateDependentStyle.) + return true; + } + const bool isRelevantLink = (aFlags & eLookForRelevantLink) && + nsCSSRuleProcessor::IsLink(element); + NodeMatchContext nodeContext(EventStates(), isRelevantLink); + if (isRelevantLink) { + // If we find an ancestor of the matched node that is a link + // during the matching process, then it's the relevant link (see + // constructor call above). + // Since we are still matching against selectors that contain + // :visited (they'll just fail), we will always find such a node + // during the selector matching process if there is a relevant + // link that can influence selector matching. + aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); + aTreeMatchContext.SetHaveRelevantLink(); + } + if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext, + SelectorMatchesFlags::NONE)) { + // to avoid greedy matching, we need to recur if this is a + // descendant or general sibling combinator and the next + // combinator is different, but we can make an exception for + // sibling, then parent, since a sibling's parent is always the + // same. + if (NS_IS_GREEDY_OPERATOR(selector->mOperator) && + selector->mNext && + selector->mNext->mOperator != selector->mOperator && + !(selector->mOperator == '~' && + NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) { + + // pretend the selector didn't match, and step through content + // while testing the same selector + + // This approach is slightly strange in that when it recurs + // it tests from the top of the content tree, down. This + // doesn't matter much for performance since most selectors + // don't match. (If most did, it might be faster...) + Element* styleScope = aTreeMatchContext.mCurrentStyleScope; + if (SelectorMatchesTree(element, selector, aTreeMatchContext, aFlags)) { + return true; + } + // We want to reset mCurrentStyleScope on aTreeMatchContext + // back to its state before the SelectorMatchesTree call, in + // case that call happens to traverse past the style scope element + // and sets it to null. + aTreeMatchContext.mCurrentStyleScope = styleScope; + } + selector = selector->mNext; + } + else { + // for adjacent sibling and child combinators, if we didn't find + // a match, we're done + if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) { + return false; // parent was required to match + } + } + prevElement = element; + } + return true; // all the selectors matched. +} + +static inline +void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector, + ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext, + AncestorFilter *ancestorFilter) +{ + if (nodeContext.mIsRelevantLink) { + data->mTreeMatchContext.SetHaveRelevantLink(); + } + if (ancestorFilter && + !ancestorFilter->MightHaveMatchingAncestor( + value.mAncestorSelectorHashes)) { + // We won't match; nothing else to do here + return; + } + if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement, + data->mScope)) { + // The selector is for a rule in a scoped style sheet, and the subject + // of the selector matching is not in its scope. + return; + } + nsCSSSelector* selector = aSelector; + if (selector->IsPseudoElement()) { + PseudoElementRuleProcessorData* pdata = + static_cast(data); + if (!pdata->mPseudoElement && selector->mPseudoClassList) { + // We can get here when calling getComputedStyle(aElt, aPseudo) if: + // + // * aPseudo is a pseudo-element that supports a user action + // pseudo-class, like "::placeholder"; + // * there is a style rule that uses a pseudo-class on this + // pseudo-element in the document, like ::placeholder:hover; and + // * aElt does not have such a pseudo-element. + // + // We know that the selector can't match, since there is no element for + // the user action pseudo-class to match against. + return; + } + if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext, + data->mTreeMatchContext, + SelectorMatchesFlags::NONE)) { + return; + } + selector = selector->mNext; + } + + SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::NONE; + if (aSelector->IsPseudoElement()) { + selectorFlags |= SelectorMatchesFlags::HAS_PSEUDO_ELEMENT; + } + if (SelectorMatches(data->mElement, selector, nodeContext, + data->mTreeMatchContext, selectorFlags)) { + nsCSSSelector *next = selector->mNext; + if (!next || + SelectorMatchesTree(data->mElement, next, + data->mTreeMatchContext, + nodeContext.mIsRelevantLink ? + SelectorMatchesTreeFlags(0) : + eLookForRelevantLink)) { + css::Declaration* declaration = value.mRule->GetDeclaration(); + declaration->SetImmutable(); + data->mRuleWalker->Forward(declaration); + // nsStyleSet will deal with the !important rule + } + } +} + +/* virtual */ void +nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData) +{ + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + if (cascade) { + NodeMatchContext nodeContext(EventStates(), + nsCSSRuleProcessor::IsLink(aData->mElement)); + cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext); + } +} + +/* virtual */ void +nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData) +{ + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + if (cascade) { + RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[ + static_cast(aData->mPseudoType)]; + if (ruleHash) { + NodeMatchContext nodeContext(EventStates(), + nsCSSRuleProcessor::IsLink(aData->mElement)); + ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext); + } + } +} + +/* virtual */ void +nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData) +{ + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + if (cascade && cascade->mAnonBoxRules.EntryCount()) { + auto entry = static_cast + (cascade->mAnonBoxRules.Search(aData->mPseudoTag)); + if (entry) { + nsTArray& rules = entry->mRules; + for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); + value != end; ++value) { + css::Declaration* declaration = value->mRule->GetDeclaration(); + declaration->SetImmutable(); + aData->mRuleWalker->Forward(declaration); + } + } + } +} + +#ifdef MOZ_XUL +/* virtual */ void +nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData) +{ + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + if (cascade && cascade->mXULTreeRules.EntryCount()) { + auto entry = static_cast + (cascade->mXULTreeRules.Search(aData->mPseudoTag)); + if (entry) { + NodeMatchContext nodeContext(EventStates(), + nsCSSRuleProcessor::IsLink(aData->mElement)); + nsTArray& rules = entry->mRules; + for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); + value != end; ++value) { + if (aData->mComparator->PseudoMatches(value->mSelector)) { + ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext, + nullptr); + } + } + } + } +} +#endif + +static inline nsRestyleHint RestyleHintForOp(char16_t oper) +{ + if (oper == char16_t('+') || oper == char16_t('~')) { + return eRestyle_LaterSiblings; + } + + if (oper != char16_t(0)) { + return eRestyle_Subtree; + } + + return eRestyle_Self; +} + +nsRestyleHint +nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData, + Element* aStatefulElement, + CSSPseudoElementType aPseudoType, + EventStates aStateMask) +{ + MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle, + "mCurrentStyleScope will need to be saved and restored after the " + "SelectorMatchesTree call"); + + bool isPseudoElement = + aPseudoType != CSSPseudoElementType::NotPseudo; + + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + // Look up the content node in the state rule list, which points to + // any (CSS2 definition) simple selector (whether or not it is the + // subject) that has a state pseudo-class on it. This means that this + // code will be matching selectors that aren't real selectors in any + // stylesheet (e.g., if there is a selector "body > p:hover > a", then + // "body > p:hover" will be in |cascade->mStateSelectors|). Note that + // |ComputeSelectorStateDependence| below determines which selectors are in + // |cascade->mStateSelectors|. + nsRestyleHint hint = nsRestyleHint(0); + if (cascade) { + StateSelector *iter = cascade->mStateSelectors.Elements(), + *end = iter + cascade->mStateSelectors.Length(); + NodeMatchContext nodeContext(aStateMask, false); + for(; iter != end; ++iter) { + nsCSSSelector* selector = iter->mSelector; + EventStates states = iter->mStates; + + if (selector->IsPseudoElement() != isPseudoElement) { + continue; + } + + nsCSSSelector* selectorForPseudo; + if (isPseudoElement) { + if (selector->PseudoType() != aPseudoType) { + continue; + } + selectorForPseudo = selector; + selector = selector->mNext; + } + + nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator); + SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::UNKNOWN; + + // If hint already includes all the bits of possibleChange, + // don't bother calling SelectorMatches, since even if it returns false + // hint won't change. + // Also don't bother calling SelectorMatches if none of the + // states passed in are relevant here. + if ((possibleChange & ~hint) && + states.HasAtLeastOneOfStates(aStateMask) && + // We can optimize away testing selectors that only involve :hover, a + // namespace, and a tag name against nodes that don't have the + // NodeHasRelevantHoverRules flag: such a selector didn't match + // the tag name or namespace the first time around (since the :hover + // didn't set the NodeHasRelevantHoverRules flag), so it won't + // match it now. Check for our selector only having :hover states, or + // the element having the hover rules flag, or the selector having + // some sort of non-namespace, non-tagname data in it. + (states != NS_EVENT_STATE_HOVER || + aStatefulElement->HasRelevantHoverRules() || + selector->mIDList || selector->mClassList || + // We generally expect an mPseudoClassList, since we have a :hover. + // The question is whether we have anything else in there. + (selector->mPseudoClassList && + (selector->mPseudoClassList->mNext || + selector->mPseudoClassList->mType != + CSSPseudoClassType::hover)) || + selector->mAttrList || selector->mNegations) && + (!isPseudoElement || + StateSelectorMatches(aStatefulElement, selectorForPseudo, + nodeContext, aData->mTreeMatchContext, + selectorFlags, nullptr, aStateMask)) && + SelectorMatches(aData->mElement, selector, nodeContext, + aData->mTreeMatchContext, selectorFlags) && + SelectorMatchesTree(aData->mElement, selector->mNext, + aData->mTreeMatchContext, + eMatchOnConditionalRestyleAncestor)) + { + hint = nsRestyleHint(hint | possibleChange); + } + } + } + return hint; +} + +nsRestyleHint +nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData) +{ + return HasStateDependentStyle(aData, + aData->mElement, + CSSPseudoElementType::NotPseudo, + aData->mStateMask); +} + +nsRestyleHint +nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) +{ + return HasStateDependentStyle(aData, + aData->mPseudoElement, + aData->mPseudoType, + aData->mStateMask); +} + +bool +nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData) +{ + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask); +} + +struct AttributeEnumData { + AttributeEnumData(AttributeRuleProcessorData *aData, + RestyleHintData& aRestyleHintData) + : data(aData), change(nsRestyleHint(0)), hintData(aRestyleHintData) {} + + AttributeRuleProcessorData *data; + nsRestyleHint change; + RestyleHintData& hintData; +}; + + +static inline nsRestyleHint +RestyleHintForSelectorWithAttributeChange(nsRestyleHint aCurrentHint, + nsCSSSelector* aSelector, + nsCSSSelector* aRightmostSelector) +{ + MOZ_ASSERT(aSelector); + + char16_t oper = aSelector->mOperator; + + if (oper == char16_t('+') || oper == char16_t('~')) { + return eRestyle_LaterSiblings; + } + + if (oper == char16_t(':')) { + return eRestyle_Subtree; + } + + if (oper != char16_t(0)) { + // Check whether the selector is in a form that supports + // eRestyle_SomeDescendants. If it isn't, return eRestyle_Subtree. + + if (aCurrentHint & eRestyle_Subtree) { + // No point checking, since we'll end up restyling the whole + // subtree anyway. + return eRestyle_Subtree; + } + + if (!aRightmostSelector) { + // aSelector wasn't a top-level selector, which means we were inside + // a :not() or :-moz-any(). We don't support that. + return eRestyle_Subtree; + } + + MOZ_ASSERT(aSelector != aRightmostSelector, + "if aSelector == aRightmostSelector then we should have " + "no operator"); + + // Check that aRightmostSelector can be passed to RestrictedSelectorMatches. + if (!aRightmostSelector->IsRestrictedSelector()) { + return eRestyle_Subtree; + } + + // We also don't support pseudo-elements on any of the selectors + // between aRightmostSelector and aSelector. + // XXX Can we lift this restriction, so that we don't have to loop + // over all the selectors? + for (nsCSSSelector* sel = aRightmostSelector->mNext; + sel != aSelector; + sel = sel->mNext) { + MOZ_ASSERT(sel, "aSelector must be reachable from aRightmostSelector"); + if (sel->PseudoType() != CSSPseudoElementType::NotPseudo) { + return eRestyle_Subtree; + } + } + + return eRestyle_SomeDescendants; + } + + return eRestyle_Self; +} + +static void +AttributeEnumFunc(nsCSSSelector* aSelector, + nsCSSSelector* aRightmostSelector, + AttributeEnumData* aData) +{ + AttributeRuleProcessorData *data = aData->data; + + if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement, + data->mScope)) { + // The selector is for a rule in a scoped style sheet, and the subject + // of the selector matching is not in its scope. + return; + } + + nsRestyleHint possibleChange = + RestyleHintForSelectorWithAttributeChange(aData->change, + aSelector, aRightmostSelector); + + // If, ignoring eRestyle_SomeDescendants, enumData->change already includes + // all the bits of possibleChange, don't bother calling SelectorMatches, since + // even if it returns false enumData->change won't change. If possibleChange + // has eRestyle_SomeDescendants, we need to call SelectorMatches(Tree) + // regardless as it might give us new selectors to append to + // mSelectorsForDescendants. + NodeMatchContext nodeContext(EventStates(), false); + if (((possibleChange & (~(aData->change) | eRestyle_SomeDescendants))) && + SelectorMatches(data->mElement, aSelector, nodeContext, + data->mTreeMatchContext, SelectorMatchesFlags::UNKNOWN) && + SelectorMatchesTree(data->mElement, aSelector->mNext, + data->mTreeMatchContext, + eMatchOnConditionalRestyleAncestor)) { + aData->change = nsRestyleHint(aData->change | possibleChange); + if (possibleChange & eRestyle_SomeDescendants) { + aData->hintData.mSelectorsForDescendants.AppendElement(aRightmostSelector); + } + } +} + +static MOZ_ALWAYS_INLINE void +EnumerateSelectors(nsTArray& aSelectors, AttributeEnumData* aData) +{ + SelectorPair *iter = aSelectors.Elements(), + *end = iter + aSelectors.Length(); + for (; iter != end; ++iter) { + AttributeEnumFunc(iter->mSelector, iter->mRightmostSelector, aData); + } +} + +static MOZ_ALWAYS_INLINE void +EnumerateSelectors(nsTArray& aSelectors, AttributeEnumData* aData) +{ + nsCSSSelector **iter = aSelectors.Elements(), + **end = iter + aSelectors.Length(); + for (; iter != end; ++iter) { + AttributeEnumFunc(*iter, nullptr, aData); + } +} + +nsRestyleHint +nsCSSRuleProcessor::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) +{ + // We could try making use of aData->mModType, but :not rules make it a bit + // of a pain to do so... So just ignore it for now. + + AttributeEnumData data(aData, aRestyleHintDataResult); + + // Don't do our special handling of certain attributes if the attr + // hasn't changed yet. + if (aData->mAttrHasChanged) { + // check for the lwtheme and lwthemetextcolor attribute on root XUL elements + if ((aData->mAttribute == nsGkAtoms::lwtheme || + aData->mAttribute == nsGkAtoms::lwthemetextcolor) && + aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL && + aData->mElement == aData->mElement->OwnerDoc()->GetRootElement()) + { + data.change = nsRestyleHint(data.change | eRestyle_Subtree); + } + + // We don't know the namespace of the attribute, and xml:lang applies to + // all elements. If the lang attribute changes, we need to restyle our + // whole subtree, since the :lang selector on our descendants can examine + // our lang attribute. + if (aData->mAttribute == nsGkAtoms::lang) { + data.change = nsRestyleHint(data.change | eRestyle_Subtree); + } + } + + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); + + // Since we get both before and after notifications for attributes, we + // don't have to ignore aData->mAttribute while matching. Just check + // whether we have selectors relevant to aData->mAttribute that we + // match. If this is the before change notification, that will catch + // rules we might stop matching; if the after change notification, the + // ones we might have started matching. + if (cascade) { + if (aData->mAttribute == nsGkAtoms::id) { + nsIAtom* id = aData->mElement->GetID(); + if (id) { + auto entry = + static_cast(cascade->mIdSelectors.Search(id)); + if (entry) { + EnumerateSelectors(entry->mSelectors, &data); + } + } + + EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data); + } + + if (aData->mAttribute == nsGkAtoms::_class && + aData->mNameSpaceID == kNameSpaceID_None) { + const nsAttrValue* otherClasses = aData->mOtherValue; + NS_ASSERTION(otherClasses || + aData->mModType == nsIDOMMutationEvent::REMOVAL, + "All class values should be StoresOwnData and parsed" + "via Element::BeforeSetAttr, so available here"); + // For WillChange, enumerate classes that will be removed to see which + // rules apply before the change. + // For Changed, enumerate classes that have been added to see which rules + // apply after the change. + // In both cases we're interested in the classes that are currently on + // the element but not in mOtherValue. + const nsAttrValue* elementClasses = aData->mElement->GetClasses(); + if (elementClasses) { + int32_t atomCount = elementClasses->GetAtomCount(); + if (atomCount > 0) { + nsTHashtable> otherClassesTable; + if (otherClasses) { + int32_t otherClassesCount = otherClasses->GetAtomCount(); + for (int32_t i = 0; i < otherClassesCount; ++i) { + otherClassesTable.PutEntry(otherClasses->AtomAt(i)); + } + } + for (int32_t i = 0; i < atomCount; ++i) { + nsIAtom* curClass = elementClasses->AtomAt(i); + if (!otherClassesTable.Contains(curClass)) { + auto entry = + static_cast + (cascade->mClassSelectors.Search(curClass)); + if (entry) { + EnumerateSelectors(entry->mSelectors, &data); + } + } + } + } + } + + EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data); + } + + auto entry = + static_cast + (cascade->mAttributeSelectors.Search(aData->mAttribute)); + if (entry) { + EnumerateSelectors(entry->mSelectors, &data); + } + } + + return data.change; +} + +/* virtual */ bool +nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext) +{ + // We don't want to do anything if there aren't any sets of rules + // cached yet, since we should not build the rule cascade too early + // (e.g., before we know whether the quirk style sheet should be + // enabled). And if there's nothing cached, it doesn't matter if + // anything changed. But in the cases where it does matter, we've + // cached a previous cache key to test against, instead of our current + // rule cascades. See bug 448281 and bug 1089417. + MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey)); + RuleCascadeData *old = mRuleCascades; + if (old) { + RefreshRuleCascade(aPresContext); + return (old != mRuleCascades); + } + + if (mPreviousCacheKey) { + // RefreshRuleCascade will get rid of mPreviousCacheKey anyway to + // maintain the invariant that we can't have both an mRuleCascades + // and an mPreviousCacheKey. But we need to hold it a little + // longer. + UniquePtr previousCacheKey( + Move(mPreviousCacheKey)); + RefreshRuleCascade(aPresContext); + + // This test is a bit pessimistic since the cache key's operator== + // just does list comparison rather than set comparison, but it + // should catch all the cases we care about (i.e., where the cascade + // order hasn't changed). Other cases will do a restyle anyway, so + // we shouldn't need to worry about posting a second. + return !mRuleCascades || // all sheets gone, but we had sheets before + mRuleCascades->mCacheKey != *previousCacheKey; + } + + return false; +} + +UniquePtr +nsCSSRuleProcessor::CloneMQCacheKey() +{ + MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey)); + + RuleCascadeData* c = mRuleCascades; + if (!c) { + // We might have an mPreviousCacheKey. It already comes from a call + // to CloneMQCacheKey, so don't bother checking + // HasFeatureConditions(). + if (mPreviousCacheKey) { + NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(), + "we shouldn't have a previous cache key unless it has " + "feature conditions"); + return MakeUnique(*mPreviousCacheKey); + } + + return UniquePtr(); + } + + if (!c->mCacheKey.HasFeatureConditions()) { + return UniquePtr(); + } + + return MakeUnique(c->mCacheKey); +} + +/* virtual */ size_t +nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + n += mSheets.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (RuleCascadeData* cascade = mRuleCascades; cascade; + cascade = cascade->mNext) { + n += cascade->SizeOfIncludingThis(aMallocSizeOf); + } + + return n; +} + +/* virtual */ size_t +nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const +{ + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + +// Append all the currently-active font face rules to aArray. Return +// true for success and false for failure. +bool +nsCSSRuleProcessor::AppendFontFaceRules( + nsPresContext *aPresContext, + nsTArray& aArray) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + if (!aArray.AppendElements(cascade->mFontFaceRules)) + return false; + } + + return true; +} + +nsCSSKeyframesRule* +nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext, + const nsString& aName) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + return cascade->mKeyframesRuleTable.Get(aName); + } + + return nullptr; +} + +nsCSSCounterStyleRule* +nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext, + const nsAString& aName) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + return cascade->mCounterStyleRuleTable.Get(aName); + } + + return nullptr; +} + +// Append all the currently-active page rules to aArray. Return +// true for success and false for failure. +bool +nsCSSRuleProcessor::AppendPageRules( + nsPresContext* aPresContext, + nsTArray& aArray) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + if (!aArray.AppendElements(cascade->mPageRules)) { + return false; + } + } + + return true; +} + +bool +nsCSSRuleProcessor::AppendFontFeatureValuesRules( + nsPresContext *aPresContext, + nsTArray& aArray) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + if (!aArray.AppendElements(cascade->mFontFeatureValuesRules)) + return false; + } + + return true; +} + +nsresult +nsCSSRuleProcessor::ClearRuleCascades() +{ + if (!mPreviousCacheKey) { + mPreviousCacheKey = CloneMQCacheKey(); + } + + // No need to remove the rule processor from the RuleProcessorCache here, + // since CSSStyleSheet::ClearRuleCascades will have called + // RuleProcessorCache::RemoveSheet() passing itself, which will catch + // this rule processor (and any others for different @-moz-document + // cache key results). + MOZ_ASSERT(!RuleProcessorCache::HasRuleProcessor(this)); + +#ifdef DEBUG + // For shared rule processors, if we've already gathered document + // rules, then they will now be out of date. We don't actually need + // them to be up-to-date (see the comment in RefreshRuleCascade), so + // record their invalidity so we can assert if we try to use them. + if (!mMustGatherDocumentRules) { + mDocumentRulesAndCacheKeyValid = false; + } +#endif + + // We rely on our caller (perhaps indirectly) to do something that + // will rebuild style data and the user font set (either + // nsIPresShell::RestyleForCSSRuleChanges or + // nsPresContext::RebuildAllStyleData). + RuleCascadeData *data = mRuleCascades; + mRuleCascades = nullptr; + while (data) { + RuleCascadeData *next = data->mNext; + delete data; + data = next; + } + return NS_OK; +} + + +// This function should return the set of states that this selector +// depends on; this is used to implement HasStateDependentStyle. It +// does NOT recur down into things like :not and :-moz-any. +inline +EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector) +{ + EventStates states; + for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + // Tree pseudo-elements overload mPseudoClassList for things that + // aren't pseudo-classes. + if (pseudoClass->mType >= CSSPseudoClassType::Count) { + continue; + } + + auto idx = static_cast(pseudoClass->mType); + states |= sPseudoClassStateDependences[idx]; + } + return states; +} + +static bool +AddSelector(RuleCascadeData* aCascade, + // The part between combinators at the top level of the selector + nsCSSSelector* aSelectorInTopLevel, + // The part we should look through (might be in :not or :-moz-any()) + nsCSSSelector* aSelectorPart, + // The right-most selector at the top level + nsCSSSelector* aRightmostSelector) +{ + // It's worth noting that this loop over negations isn't quite + // optimal for two reasons. One, we could add something to one of + // these lists twice, which means we'll check it twice, but I don't + // think that's worth worrying about. (We do the same for multiple + // attribute selectors on the same attribute.) Two, we don't really + // need to check negations past the first in the current + // implementation (and they're rare as well), but that might change + // in the future if :not() is extended. + for (nsCSSSelector* negation = aSelectorPart; negation; + negation = negation->mNegations) { + // Track both document states and attribute dependence in pseudo-classes. + for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + switch (pseudoClass->mType) { + case CSSPseudoClassType::mozLocaleDir: { + aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE; + break; + } + case CSSPseudoClassType::mozWindowInactive: { + aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; + break; + } + case CSSPseudoClassType::mozTableBorderNonzero: { + nsTArray *array = + aCascade->AttributeListFor(nsGkAtoms::border); + if (!array) { + return false; + } + array->AppendElement(SelectorPair(aSelectorInTopLevel, + aRightmostSelector)); + break; + } + default: { + break; + } + } + } + + // Build mStateSelectors. + EventStates dependentStates = ComputeSelectorStateDependence(*negation); + if (!dependentStates.IsEmpty()) { + aCascade->mStateSelectors.AppendElement( + nsCSSRuleProcessor::StateSelector(dependentStates, + aSelectorInTopLevel)); + } + + // Build mIDSelectors + if (negation == aSelectorInTopLevel) { + for (nsAtomList* curID = negation->mIDList; curID; + curID = curID->mNext) { + auto entry = static_cast + (aCascade->mIdSelectors.Add(curID->mAtom, fallible)); + if (entry) { + entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel, + aRightmostSelector)); + } + } + } else if (negation->mIDList) { + aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel); + } + + // Build mClassSelectors + if (negation == aSelectorInTopLevel) { + for (nsAtomList* curClass = negation->mClassList; curClass; + curClass = curClass->mNext) { + auto entry = static_cast + (aCascade->mClassSelectors.Add(curClass->mAtom, fallible)); + if (entry) { + entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel, + aRightmostSelector)); + } + } + } else if (negation->mClassList) { + aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel); + } + + // Build mAttributeSelectors. + for (nsAttrSelector *attr = negation->mAttrList; attr; + attr = attr->mNext) { + nsTArray *array = + aCascade->AttributeListFor(attr->mCasedAttr); + if (!array) { + return false; + } + array->AppendElement(SelectorPair(aSelectorInTopLevel, + aRightmostSelector)); + if (attr->mLowercaseAttr != attr->mCasedAttr) { + array = aCascade->AttributeListFor(attr->mLowercaseAttr); + if (!array) { + return false; + } + array->AppendElement(SelectorPair(aSelectorInTopLevel, + aRightmostSelector)); + } + } + + // Recur through any :-moz-any selectors + for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; + pseudoClass; pseudoClass = pseudoClass->mNext) { + if (pseudoClass->mType == CSSPseudoClassType::any) { + for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) { + nsCSSSelector *s = l->mSelectors; + if (!AddSelector(aCascade, aSelectorInTopLevel, s, + aRightmostSelector)) { + return false; + } + } + } + } + } + + return true; +} + +static bool +AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade) +{ + RuleCascadeData * const cascade = aCascade; + + // Build the rule hash. + CSSPseudoElementType pseudoType = aRuleInfo->mSelector->PseudoType(); + if (MOZ_LIKELY(pseudoType == CSSPseudoElementType::NotPseudo)) { + cascade->mRuleHash.AppendRule(*aRuleInfo); + } else if (pseudoType < CSSPseudoElementType::Count) { + RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[ + static_cast(pseudoType)]; + if (!ruleHash) { + ruleHash = new RuleHash(cascade->mQuirksMode); + if (!ruleHash) { + // Out of memory; give up + return false; + } + } + NS_ASSERTION(aRuleInfo->mSelector->mNext, + "Must have mNext; parser screwed up"); + NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':', + "Unexpected mNext combinator"); + ruleHash->AppendRule(*aRuleInfo); + } else if (pseudoType == CSSPseudoElementType::AnonBox) { + NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag && + !aRuleInfo->mSelector->mIDList && + !aRuleInfo->mSelector->mClassList && + !aRuleInfo->mSelector->mPseudoClassList && + !aRuleInfo->mSelector->mAttrList && + !aRuleInfo->mSelector->mNegations && + !aRuleInfo->mSelector->mNext && + aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown, + "Parser messed up with anon box selector"); + + // Index doesn't matter here, since we'll just be walking these + // rules in order; just pass 0. + AppendRuleToTagTable(&cascade->mAnonBoxRules, + aRuleInfo->mSelector->mLowercaseTag, + RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode)); + } else { +#ifdef MOZ_XUL + NS_ASSERTION(pseudoType == CSSPseudoElementType::XULTree, + "Unexpected pseudo type"); + // Index doesn't matter here, since we'll just be walking these + // rules in order; just pass 0. + AppendRuleToTagTable(&cascade->mXULTreeRules, + aRuleInfo->mSelector->mLowercaseTag, + RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode)); +#else + NS_NOTREACHED("Unexpected pseudo type"); +#endif + } + + for (nsCSSSelector* selector = aRuleInfo->mSelector; + selector; selector = selector->mNext) { + if (selector->IsPseudoElement()) { + CSSPseudoElementType pseudo = selector->PseudoType(); + if (pseudo >= CSSPseudoElementType::Count || + !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) { + NS_ASSERTION(!selector->mNegations, "Shouldn't have negations"); + // We do store selectors ending with pseudo-elements that allow :hover + // and :active after them in the hashtables corresponding to that + // selector's mNext (i.e. the thing that matches against the element), + // but we want to make sure that selectors for any other kinds of + // pseudo-elements don't end up in the hashtables. In particular, tree + // pseudos store strange things in mPseudoClassList that we don't want + // to try to match elements against. + continue; + } + } + if (!AddSelector(cascade, selector, selector, aRuleInfo->mSelector)) { + return false; + } + } + + return true; +} + +struct PerWeightDataListItem : public RuleSelectorPair { + PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector) + : RuleSelectorPair(aRule, aSelector) + , mNext(nullptr) + {} + // No destructor; these are arena-allocated + + + // Placement new to arena allocate the PerWeightDataListItem + void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW { + void *mem; + PL_ARENA_ALLOCATE(mem, &aArena, aSize); + return mem; + } + + PerWeightDataListItem *mNext; +}; + +struct PerWeightData { + PerWeightData() + : mRuleSelectorPairs(nullptr) + , mTail(&mRuleSelectorPairs) + {} + + int32_t mWeight; + PerWeightDataListItem *mRuleSelectorPairs; + PerWeightDataListItem **mTail; +}; + +struct RuleByWeightEntry : public PLDHashEntryHdr { + PerWeightData data; // mWeight is key, mRuleSelectorPairs are value +}; + +static PLDHashNumber +HashIntKey(const void *key) +{ + return PLDHashNumber(NS_PTR_TO_INT32(key)); +} + +static bool +MatchWeightEntry(const PLDHashEntryHdr *hdr, const void *key) +{ + const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr; + return entry->data.mWeight == NS_PTR_TO_INT32(key); +} + +static void +InitWeightEntry(PLDHashEntryHdr *hdr, const void *key) +{ + RuleByWeightEntry* entry = static_cast(hdr); + new (KnownNotNull, entry) RuleByWeightEntry(); +} + +static const PLDHashTableOps gRulesByWeightOps = { + HashIntKey, + MatchWeightEntry, + PLDHashTable::MoveEntryStub, + PLDHashTable::ClearEntryStub, + InitWeightEntry +}; + +struct CascadeEnumData { + CascadeEnumData(nsPresContext* aPresContext, + nsTArray& aFontFaceRules, + nsTArray& aKeyframesRules, + nsTArray& aFontFeatureValuesRules, + nsTArray& aPageRules, + nsTArray& aCounterStyleRules, + nsTArray& aDocumentRules, + nsMediaQueryResultCacheKey& aKey, + nsDocumentRuleResultCacheKey& aDocumentKey, + SheetType aSheetType, + bool aMustGatherDocumentRules) + : mPresContext(aPresContext), + mFontFaceRules(aFontFaceRules), + mKeyframesRules(aKeyframesRules), + mFontFeatureValuesRules(aFontFeatureValuesRules), + mPageRules(aPageRules), + mCounterStyleRules(aCounterStyleRules), + mDocumentRules(aDocumentRules), + mCacheKey(aKey), + mDocumentCacheKey(aDocumentKey), + mRulesByWeight(&gRulesByWeightOps, sizeof(RuleByWeightEntry), 32), + mSheetType(aSheetType), + mMustGatherDocumentRules(aMustGatherDocumentRules) + { + // Initialize our arena + PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena", + NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE); + } + + ~CascadeEnumData() + { + PL_FinishArenaPool(&mArena); + } + + nsPresContext* mPresContext; + nsTArray& mFontFaceRules; + nsTArray& mKeyframesRules; + nsTArray& mFontFeatureValuesRules; + nsTArray& mPageRules; + nsTArray& mCounterStyleRules; + nsTArray& mDocumentRules; + nsMediaQueryResultCacheKey& mCacheKey; + nsDocumentRuleResultCacheKey& mDocumentCacheKey; + PLArenaPool mArena; + // Hooray, a manual PLDHashTable since nsClassHashtable doesn't + // provide a getter that gives me a *reference* to the value. + PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists + SheetType mSheetType; + bool mMustGatherDocumentRules; +}; + +/** + * Recursively traverses rules in order to: + * (1) add any @-moz-document rules into data->mDocumentRules. + * (2) record any @-moz-document rules whose conditions evaluate to true + * on data->mDocumentCacheKey. + * + * See also CascadeRuleEnumFunc below, which calls us via + * EnumerateRulesForwards. If modifying this function you may need to + * update CascadeRuleEnumFunc too. + */ +static bool +GatherDocRuleEnumFunc(css::Rule* aRule, void* aData) +{ + CascadeEnumData* data = (CascadeEnumData*)aData; + int32_t type = aRule->GetType(); + + MOZ_ASSERT(data->mMustGatherDocumentRules, + "should only call GatherDocRuleEnumFunc if " + "mMustGatherDocumentRules is true"); + + if (css::Rule::MEDIA_RULE == type || + css::Rule::SUPPORTS_RULE == type) { + css::GroupRule* groupRule = static_cast(aRule); + if (!groupRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) { + return false; + } + } + else if (css::Rule::DOCUMENT_RULE == type) { + css::DocumentRule* docRule = static_cast(aRule); + if (!data->mDocumentRules.AppendElement(docRule)) { + return false; + } + if (docRule->UseForPresentation(data->mPresContext)) { + if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) { + return false; + } + } + if (!docRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) { + return false; + } + } + return true; +} + +/* + * This enumerates style rules in a sheet (and recursively into any + * grouping rules) in order to: + * (1) add any style rules, in order, into data->mRulesByWeight (for + * the primary CSS cascade), where they are separated by weight + * but kept in order per-weight, and + * (2) add any @font-face rules, in order, into data->mFontFaceRules. + * (3) add any @keyframes rules, in order, into data->mKeyframesRules. + * (4) add any @font-feature-value rules, in order, + * into data->mFontFeatureValuesRules. + * (5) add any @page rules, in order, into data->mPageRules. + * (6) add any @counter-style rules, in order, into data->mCounterStyleRules. + * (7) add any @-moz-document rules into data->mDocumentRules. + * (8) record any @-moz-document rules whose conditions evaluate to true + * on data->mDocumentCacheKey. + * + * See also GatherDocRuleEnumFunc above, which we call to traverse into + * @-moz-document rules even if their (or an ancestor's) condition + * fails. This means we might look at the result of some @-moz-document + * rules that don't actually affect whether a RuleProcessorCache lookup + * is a hit or a miss. The presence of @-moz-document rules inside + * @media etc. rules should be rare, and looking at all of them in the + * sheets lets us avoid the complication of having different document + * cache key results for different media. + * + * If modifying this function you may need to update + * GatherDocRuleEnumFunc too. + */ +static bool +CascadeRuleEnumFunc(css::Rule* aRule, void* aData) +{ + CascadeEnumData* data = (CascadeEnumData*)aData; + int32_t type = aRule->GetType(); + + if (css::Rule::STYLE_RULE == type) { + css::StyleRule* styleRule = static_cast(aRule); + + for (nsCSSSelectorList *sel = styleRule->Selector(); + sel; sel = sel->mNext) { + int32_t weight = sel->mWeight; + auto entry = static_cast + (data->mRulesByWeight.Add(NS_INT32_TO_PTR(weight), fallible)); + if (!entry) + return false; + entry->data.mWeight = weight; + // entry->data.mRuleSelectorPairs should be linked in forward order; + // entry->data.mTail is the slot to write to. + auto* newItem = + new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors); + if (newItem) { + *(entry->data.mTail) = newItem; + entry->data.mTail = &newItem->mNext; + } + } + } + else if (css::Rule::MEDIA_RULE == type || + css::Rule::SUPPORTS_RULE == type) { + css::GroupRule* groupRule = static_cast(aRule); + const bool use = + groupRule->UseForPresentation(data->mPresContext, data->mCacheKey); + if (use || data->mMustGatherDocumentRules) { + if (!groupRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc : + GatherDocRuleEnumFunc, + aData)) { + return false; + } + } + } + else if (css::Rule::DOCUMENT_RULE == type) { + css::DocumentRule* docRule = static_cast(aRule); + if (data->mMustGatherDocumentRules) { + if (!data->mDocumentRules.AppendElement(docRule)) { + return false; + } + } + const bool use = docRule->UseForPresentation(data->mPresContext); + if (use && data->mMustGatherDocumentRules) { + if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) { + return false; + } + } + if (use || data->mMustGatherDocumentRules) { + if (!docRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc + : GatherDocRuleEnumFunc, + aData)) { + return false; + } + } + } + else if (css::Rule::FONT_FACE_RULE == type) { + nsCSSFontFaceRule *fontFaceRule = static_cast(aRule); + nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement(); + if (!ptr) + return false; + ptr->mRule = fontFaceRule; + ptr->mSheetType = data->mSheetType; + } + else if (css::Rule::KEYFRAMES_RULE == type) { + nsCSSKeyframesRule *keyframesRule = + static_cast(aRule); + if (!data->mKeyframesRules.AppendElement(keyframesRule)) { + return false; + } + } + else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) { + nsCSSFontFeatureValuesRule *fontFeatureValuesRule = + static_cast(aRule); + if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) { + return false; + } + } + else if (css::Rule::PAGE_RULE == type) { + nsCSSPageRule* pageRule = static_cast(aRule); + if (!data->mPageRules.AppendElement(pageRule)) { + return false; + } + } + else if (css::Rule::COUNTER_STYLE_RULE == type) { + nsCSSCounterStyleRule* counterStyleRule = + static_cast(aRule); + if (!data->mCounterStyleRules.AppendElement(counterStyleRule)) { + return false; + } + } + return true; +} + +/* static */ bool +nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeEnumData* aData) +{ + if (aSheet->IsApplicable() && + aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) && + aSheet->mInner) { + CSSStyleSheet* child = aSheet->mInner->mFirstChild; + while (child) { + CascadeSheet(child, aData); + child = child->mNext; + } + + if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc, + aData)) + return false; + } + return true; +} + +static int CompareWeightData(const void* aArg1, const void* aArg2, + void* closure) +{ + const PerWeightData* arg1 = static_cast(aArg1); + const PerWeightData* arg2 = static_cast(aArg2); + return arg1->mWeight - arg2->mWeight; // put lower weight first +} + +RuleCascadeData* +nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) +{ + // FIXME: Make this infallible! + + // If anything changes about the presentation context, we will be + // notified. Otherwise, our cache is valid if mLastPresContext + // matches aPresContext. (The only rule processors used for multiple + // pres contexts are for XBL. These rule processors are probably less + // likely to have @media rules, and thus the cache is pretty likely to + // hit instantly even when we're switching between pres contexts.) + + if (!mRuleCascades || aPresContext != mLastPresContext) { + RefreshRuleCascade(aPresContext); + } + mLastPresContext = aPresContext; + + return mRuleCascades; +} + +void +nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext) +{ + // Having RuleCascadeData objects be per-medium (over all variation + // caused by media queries, handled through mCacheKey) works for now + // since nsCSSRuleProcessor objects are per-document. (For a given + // set of stylesheets they can vary based on medium (@media) or + // document (@-moz-document).) + + for (RuleCascadeData **cascadep = &mRuleCascades, *cascade; + (cascade = *cascadep); cascadep = &cascade->mNext) { + if (cascade->mCacheKey.Matches(aPresContext)) { + // Ensure that the current one is always mRuleCascades. + *cascadep = cascade->mNext; + cascade->mNext = mRuleCascades; + mRuleCascades = cascade; + + return; + } + } + + // We're going to make a new rule cascade; this means that we should + // now stop using the previous cache key that we're holding on to from + // the last time we had rule cascades. + mPreviousCacheKey = nullptr; + + if (mSheets.Length() != 0) { + nsAutoPtr newCascade( + new RuleCascadeData(aPresContext->Medium(), + eCompatibility_NavQuirks == aPresContext->CompatibilityMode())); + if (newCascade) { + CascadeEnumData data(aPresContext, newCascade->mFontFaceRules, + newCascade->mKeyframesRules, + newCascade->mFontFeatureValuesRules, + newCascade->mPageRules, + newCascade->mCounterStyleRules, + mDocumentRules, + newCascade->mCacheKey, + mDocumentCacheKey, + mSheetType, + mMustGatherDocumentRules); + + for (uint32_t i = 0; i < mSheets.Length(); ++i) { + if (!CascadeSheet(mSheets.ElementAt(i), &data)) + return; /* out of memory */ + } + + // Sort the hash table of per-weight linked lists by weight. + uint32_t weightCount = data.mRulesByWeight.EntryCount(); + auto weightArray = MakeUnique(weightCount); + int32_t j = 0; + for (auto iter = data.mRulesByWeight.Iter(); !iter.Done(); iter.Next()) { + auto entry = static_cast(iter.Get()); + weightArray[j++] = entry->data; + } + NS_QuickSort(weightArray.get(), weightCount, sizeof(PerWeightData), + CompareWeightData, nullptr); + + // Put things into the rule hash. + // The primary sort is by weight... + for (uint32_t i = 0; i < weightCount; ++i) { + // and the secondary sort is by order. mRuleSelectorPairs is already in + // the right order.. + for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs; + cur; + cur = cur->mNext) { + if (!AddRule(cur, newCascade)) + return; /* out of memory */ + } + } + + // Build mKeyframesRuleTable. + for (nsTArray::size_type i = 0, + iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) { + nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i]; + newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule); + } + + // Build mCounterStyleRuleTable + for (nsTArray::size_type i = 0, + iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) { + nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i]; + newCascade->mCounterStyleRuleTable.Put(rule->GetName(), rule); + } + + // mMustGatherDocumentRules controls whether we build mDocumentRules + // and mDocumentCacheKey so that they can be used as keys by the + // RuleProcessorCache, as obtained by TakeDocumentRulesAndCacheKey + // later. We set it to false just below so that we only do this + // the first time we build a RuleProcessorCache for a shared rule + // processor. + // + // An up-to-date mDocumentCacheKey is only needed if we + // are still in the RuleProcessorCache (as we store a copy of the + // cache key in the RuleProcessorCache), and an up-to-date + // mDocumentRules is only needed at the time TakeDocumentRulesAndCacheKey + // is called, which is immediately after the rule processor is created + // (by nsStyleSet). + // + // Note that when nsCSSRuleProcessor::ClearRuleCascades is called, + // by CSSStyleSheet::ClearRuleCascades, we will have called + // RuleProcessorCache::RemoveSheet, which will remove the rule + // processor from the cache. (This is because the list of document + // rules now may not match the one used as they key in the + // RuleProcessorCache.) + // + // Thus, as we'll no longer be in the RuleProcessorCache, and we won't + // have TakeDocumentRulesAndCacheKey called on us, we don't need to ensure + // mDocumentCacheKey and mDocumentRules are up-to-date after the + // first time GetRuleCascade is called. + if (mMustGatherDocumentRules) { + mDocumentRules.Sort(); + mDocumentCacheKey.Finalize(); + mMustGatherDocumentRules = false; +#ifdef DEBUG + mDocumentRulesAndCacheKeyValid = true; +#endif + } + + // Ensure that the current one is always mRuleCascades. + newCascade->mNext = mRuleCascades; + mRuleCascades = newCascade.forget(); + } + } + return; +} + +/* static */ bool +nsCSSRuleProcessor::SelectorListMatches(Element* aElement, + TreeMatchContext& aTreeMatchContext, + nsCSSSelectorList* aSelectorList) +{ + MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle, + "mCurrentStyleScope will need to be saved and restored after the " + "SelectorMatchesTree call"); + + while (aSelectorList) { + nsCSSSelector* sel = aSelectorList->mSelectors; + NS_ASSERTION(sel, "Should have *some* selectors"); + NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called"); + NodeMatchContext nodeContext(EventStates(), false); + if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext, + SelectorMatchesFlags::NONE)) { + nsCSSSelector* next = sel->mNext; + if (!next || + SelectorMatchesTree(aElement, next, aTreeMatchContext, + SelectorMatchesTreeFlags(0))) { + return true; + } + } + + aSelectorList = aSelectorList->mNext; + } + + return false; +} + +void +nsCSSRuleProcessor::TakeDocumentRulesAndCacheKey( + nsPresContext* aPresContext, + nsTArray& aDocumentRules, + nsDocumentRuleResultCacheKey& aCacheKey) +{ + MOZ_ASSERT(mIsShared); + + GetRuleCascade(aPresContext); + MOZ_ASSERT(mDocumentRulesAndCacheKeyValid); + + aDocumentRules.Clear(); + aDocumentRules.SwapElements(mDocumentRules); + aCacheKey.Swap(mDocumentCacheKey); + +#ifdef DEBUG + mDocumentRulesAndCacheKeyValid = false; +#endif +} + +void +nsCSSRuleProcessor::AddStyleSetRef() +{ + MOZ_ASSERT(mIsShared); + if (++mStyleSetRefCnt == 1) { + RuleProcessorCache::StopTracking(this); + } +} + +void +nsCSSRuleProcessor::ReleaseStyleSetRef() +{ + MOZ_ASSERT(mIsShared); + MOZ_ASSERT(mStyleSetRefCnt > 0); + if (--mStyleSetRefCnt == 0 && mInRuleProcessorCache) { + RuleProcessorCache::StartTracking(this); + } +} + +// TreeMatchContext and AncestorFilter out of line methods +void +TreeMatchContext::InitAncestors(Element *aElement) +{ + MOZ_ASSERT(!mAncestorFilter.mFilter); + MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty()); + MOZ_ASSERT(mStyleScopes.IsEmpty()); + + mAncestorFilter.mFilter = new AncestorFilter::Filter(); + + if (MOZ_LIKELY(aElement)) { + MOZ_ASSERT(aElement->GetUncomposedDoc() || + aElement->HasFlag(NODE_IS_IN_SHADOW_TREE), + "aElement must be in the document or in shadow tree " + "for the assumption that GetParentNode() is non-null " + "on all element ancestors of aElement to be true"); + // Collect up the ancestors + AutoTArray ancestors; + Element* cur = aElement; + do { + ancestors.AppendElement(cur); + cur = cur->GetParentElementCrossingShadowRoot(); + } while (cur); + + // Now push them in reverse order. + for (uint32_t i = ancestors.Length(); i-- != 0; ) { + mAncestorFilter.PushAncestor(ancestors[i]); + PushStyleScope(ancestors[i]); + } + } +} + +void +TreeMatchContext::InitStyleScopes(Element* aElement) +{ + MOZ_ASSERT(mStyleScopes.IsEmpty()); + + if (MOZ_LIKELY(aElement)) { + // Collect up the ancestors + AutoTArray ancestors; + Element* cur = aElement; + do { + ancestors.AppendElement(cur); + cur = cur->GetParentElementCrossingShadowRoot(); + } while (cur); + + // Now push them in reverse order. + for (uint32_t i = ancestors.Length(); i-- != 0; ) { + PushStyleScope(ancestors[i]); + } + } +} + +void +AncestorFilter::PushAncestor(Element *aElement) +{ + MOZ_ASSERT(mFilter); + + uint32_t oldLength = mHashes.Length(); + + mPopTargets.AppendElement(oldLength); +#ifdef DEBUG + mElements.AppendElement(aElement); +#endif + mHashes.AppendElement(aElement->NodeInfo()->NameAtom()->hash()); + nsIAtom *id = aElement->GetID(); + if (id) { + mHashes.AppendElement(id->hash()); + } + const nsAttrValue *classes = aElement->GetClasses(); + if (classes) { + uint32_t classCount = classes->GetAtomCount(); + for (uint32_t i = 0; i < classCount; ++i) { + mHashes.AppendElement(classes->AtomAt(i)->hash()); + } + } + + uint32_t newLength = mHashes.Length(); + for (uint32_t i = oldLength; i < newLength; ++i) { + mFilter->add(mHashes[i]); + } +} + +void +AncestorFilter::PopAncestor() +{ + MOZ_ASSERT(!mPopTargets.IsEmpty()); + MOZ_ASSERT(mPopTargets.Length() == mElements.Length()); + + uint32_t popTargetLength = mPopTargets.Length(); + uint32_t newLength = mPopTargets[popTargetLength-1]; + + mPopTargets.TruncateLength(popTargetLength-1); +#ifdef DEBUG + mElements.TruncateLength(popTargetLength-1); +#endif + + uint32_t oldLength = mHashes.Length(); + for (uint32_t i = newLength; i < oldLength; ++i) { + mFilter->remove(mHashes[i]); + } + mHashes.TruncateLength(newLength); +} + +#ifdef DEBUG +void +AncestorFilter::AssertHasAllAncestors(Element *aElement) const +{ + Element* cur = aElement->GetParentElementCrossingShadowRoot(); + while (cur) { + MOZ_ASSERT(mElements.Contains(cur)); + cur = cur->GetParentElementCrossingShadowRoot(); + } +} + +void +TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const +{ + if (aElement->IsInNativeAnonymousSubtree()) { + // Document style sheets are never applied to native anonymous content, + // so it's not possible for them to be in a + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    + diff --git a/layout/style/test/ccd-standards.html b/layout/style/test/ccd-standards.html new file mode 100644 index 000000000..ae6f443a2 --- /dev/null +++ b/layout/style/test/ccd-standards.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    +
    +

    +

    +

    + diff --git a/layout/style/test/ccd.sjs b/layout/style/test/ccd.sjs new file mode 100644 index 000000000..058393e82 --- /dev/null +++ b/layout/style/test/ccd.sjs @@ -0,0 +1,73 @@ +const DEBUG_all_valid = false; +const DEBUG_all_stub = false; + +function handleRequest(request, response) +{ + // Decode the query string to know what test we're doing. + + // character 1: 'I' = text/css response, 'J' = text/html response + let responseCSS = (request.queryString[0] == 'I'); + + // character 2: redirection type - we only care about whether we're + // ultimately same-origin with the requesting document ('A', 'D') or + // not ('B', 'C'). + let sameOrigin = (request.queryString[1] == 'A' || + request.queryString[1] == 'D'); + + // character 3: '1' = syntactically valid, '2' = invalid, '3' = http error + let malformed = (request.queryString[2] == '2'); + let httpError = (request.queryString[2] == '3'); + + // character 4: loaded with or @import (no action required) + + // character 5: loading document mode: 'q' = quirks, 's' = standards + let quirksMode = (request.queryString[4] == 'q'); + + // Our response contains a CSS rule that selects an element whose + // ID is the first four characters of the query string. + let selector = '#' + request.queryString.substring(0,4); + + // "Malformed" responses wrap the CSS rule in the construct + // {} ... + // This mimics what the CSS parser might see if an actual HTML + // document were fed to it. Because CSS parsers recover from + // errors by skipping tokens until they find something + // recognizable, a style rule appearing where I wrote '...' above + // will be honored! + let leader = (malformed ? '{}' : ''); + let trailer = (malformed ? '' : ''); + + // Standards mode documents will ignore the style sheet if it is being + // served as text/html (regardless of its contents). Quirks mode + // documents will ignore the style sheet if it is being served as + // text/html _and_ it is not same-origin. Regardless, style sheets + // are ignored if they come as the body of an HTTP error response. + // + // Style sheets that should be ignored paint the element red; those + // that should be honored paint it lime. + let color = ((responseCSS || (quirksMode && sameOrigin)) && !httpError + ? 'lime' : 'red'); + + // For debugging the test itself, we have the capacity to make every style + // sheet well-formed, or every style sheet do nothing. + if (DEBUG_all_valid) { + // In this mode, every test chip should turn blue. + response.setHeader('Content-Type', 'text/css'); + response.write(selector + '{background-color:blue}\n'); + } else if (DEBUG_all_stub) { + // In this mode, every test chip for a case where the true test + // sheet would be honored, should turn red. + response.setHeader('Content-Type', 'text/css'); + response.write(selector + '{}\n'); + } else { + // Normal operation. + if (httpError) + response.setStatusLine(request.httpVersion, 500, + "Internal Server Error"); + response.setHeader('Content-Type', + responseCSS ? 'text/css' : 'text/html'); + response.write(leader + selector + + '{background-color:' + color + '}' + + trailer + '\n'); + } +} diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js new file mode 100644 index 000000000..4336f4abd --- /dev/null +++ b/layout/style/test/chrome/bug418986-2.js @@ -0,0 +1,314 @@ +// # Bug 418986, part 2. + +/* jshint esnext:true */ +/* jshint loopfunc:true */ +/* global window, screen, ok, SpecialPowers, matchMedia */ + +// Expected values. Format: [name, pref_off_value, pref_on_value] +// If pref_*_value is an array with two values, then we will match +// any value in between those two values. If a value is null, then +// we skip the media query. +var expected_values = [ + ["color", null, 8], + ["color-index", null, 0], + ["aspect-ratio", null, window.innerWidth + "/" + window.innerHeight], + ["device-aspect-ratio", screen.width + "/" + screen.height, + window.innerWidth + "/" + window.innerHeight], + ["device-height", screen.height + "px", window.innerHeight + "px"], + ["device-width", screen.width + "px", window.innerWidth + "px"], + ["grid", null, 0], + ["height", window.innerHeight + "px", window.innerHeight + "px"], + ["monochrome", null, 0], + // Square is defined as portrait: + ["orientation", null, + window.innerWidth > window.innerHeight ? + "landscape" : "portrait"], + ["resolution", null, "96dpi"], + ["resolution", [0.999 * window.devicePixelRatio + "dppx", + 1.001 * window.devicePixelRatio + "dppx"], "1dppx"], + ["width", window.innerWidth + "px", window.innerWidth + "px"], + ["-moz-device-pixel-ratio", window.devicePixelRatio, 1], + ["-moz-device-orientation", screen.width > screen.height ? + "landscape" : "portrait", + window.innerWidth > window.innerHeight ? + "landscape" : "portrait"] +]; + +// These media queries return value 0 or 1 when the pref is off. +// When the pref is on, they should not match. +var suppressed_toggles = [ + "-moz-mac-graphite-theme", + // Not available on most OSs. +// "-moz-maemo-classic", + "-moz-scrollbar-end-backward", + "-moz-scrollbar-end-forward", + "-moz-scrollbar-start-backward", + "-moz-scrollbar-start-forward", + "-moz-scrollbar-thumb-proportional", + "-moz-touch-enabled", + "-moz-windows-compositor", + "-moz-windows-default-theme", + "-moz-windows-glass", +]; + +// Possible values for '-moz-os-version' +var windows_versions = [ + "windows-xp", + "windows-vista", + "windows-win7", + "windows-win8", + "windows-win10", +]; + +// Possible values for '-moz-windows-theme' +var windows_themes = [ + "aero", + "aero-lite", + "luna-blue", + "luna-olive", + "luna-silver", + "royale", + "generic", + "zune" +]; + +// Read the current OS. +var OS = SpecialPowers.Services.appinfo.OS; + +// If we are using Windows, add an extra toggle only +// available on that OS. +if (OS === "WINNT") { + suppressed_toggles.push("-moz-windows-classic"); +} + +// __keyValMatches(key, val)__. +// Runs a media query and returns true if key matches to val. +var keyValMatches = (key, val) => matchMedia("(" + key + ":" + val +")").matches; + +// __testMatch(key, val)__. +// Attempts to run a media query match for the given key and value. +// If value is an array of two elements [min max], then matches any +// value in-between. +var testMatch = function (key, val) { + if (val === null) { + return; + } else if (Array.isArray(val)) { + ok(keyValMatches("min-" + key, val[0]) && keyValMatches("max-" + key, val[1]), + "Expected " + key + " between " + val[0] + " and " + val[1]); + } else { + ok(keyValMatches(key, val), "Expected " + key + ":" + val); + } +}; + +// __testToggles(resisting)__. +// Test whether we are able to match the "toggle" media queries. +var testToggles = function (resisting) { + suppressed_toggles.forEach( + function (key) { + var exists = keyValMatches(key, 0) || keyValMatches(key, 1); + if (resisting) { + ok(!exists, key + " should not exist."); + } else { + ok(exists, key + " should exist."); + } + }); +}; + +// __testWindowsSpecific__. +// Runs a media query on the queryName with the given possible matching values. +var testWindowsSpecific = function (resisting, queryName, possibleValues) { + let foundValue = null; + possibleValues.forEach(function (val) { + if (keyValMatches(queryName, val)) { + foundValue = val; + } + }); + if (resisting) { + ok(!foundValue, queryName + " should have no match"); + } else { + ok(foundValue, foundValue ? ("Match found: '" + queryName + ":" + foundValue + "'") + : "Should have a match for '" + queryName + "'"); + } +}; + +// __generateHtmlLines(resisting)__. +// Create a series of div elements that look like: +// `
    resolution
    `, +// where each line corresponds to a different media query. +var generateHtmlLines = function (resisting) { + let lines = ""; + expected_values.forEach( + function ([key, offVal, onVal]) { + let val = resisting ? onVal : offVal; + if (val) { + lines += "
    " + key + "
    \n"; + } + }); + suppressed_toggles.forEach( + function (key) { + lines += "
    " + key + "
    \n"; + }); + if (OS === "WINNT") { + lines += "
    -moz-os-version
    "; + lines += "
    -moz-windows-theme
    "; + } + return lines; +}; + +// __cssLine__. +// Creates a line of css that looks something like +// `@media (resolution: 1ppx) { .spoof#resolution { background-color: green; } }`. +var cssLine = function (query, clazz, id, color) { + return "@media " + query + " { ." + clazz + "#" + id + + " { background-color: " + color + "; } }\n"; +}; + +// __constructQuery(key, val)__. +// Creates a CSS media query from key and val. If key is an array of +// two elements, constructs a range query (using min- and max-). +var constructQuery = function (key, val) { + return Array.isArray(val) ? + "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")" : + "(" + key + ": " + val + ")"; +}; + +// __mediaQueryCSSLine(key, val, color)__. +// Creates a line containing a CSS media query and a CSS expression. +var mediaQueryCSSLine = function (key, val, color) { + if (val === null) { + return ""; + } + return cssLine(constructQuery(key, val), "spoof", key, color); +}; + +// __suppressedMediaQueryCSSLine(key, color)__. +// Creates a CSS line that matches the existence of a +// media query that is supposed to be suppressed. +var suppressedMediaQueryCSSLine = function (key, color, suppressed) { + let query = "(" + key + ": 0), (" + key + ": 1)"; + return cssLine(query, "suppress", key, color); +}; + +// __generateCSSLines(resisting)__. +// Creates a series of lines of CSS, each of which corresponds to +// a different media query. If the query produces a match to the +// expected value, then the element will be colored green. +var generateCSSLines = function (resisting) { + let lines = ".spoof { background-color: red;}\n"; + expected_values.forEach( + function ([key, offVal, onVal]) { + lines += mediaQueryCSSLine(key, resisting ? onVal : offVal, "green"); + }); + lines += ".suppress { background-color: " + (resisting ? "green" : "red") + ";}\n"; + suppressed_toggles.forEach( + function (key) { + lines += suppressedMediaQueryCSSLine(key, resisting ? "red" : "green"); + }); + if (OS === "WINNT") { + lines += ".windows { background-color: " + (resisting ? "green" : "red") + ";}\n"; + lines += windows_versions.map(val => "(-moz-os-version: " + val + ")").join(", ") + + " { #-moz-os-version { background-color: " + (resisting ? "red" : "green") + ";} }\n"; + lines += windows_themes.map(val => "(-moz-windows-theme: " + val + ")").join(",") + + " { #-moz-windows-theme { background-color: " + (resisting ? "red" : "green") + ";} }\n"; + } + return lines; +}; + +// __green__. +// Returns the computed color style corresponding to green. +var green = (function () { + let temp = document.createElement("span"); + temp.style.backgroundColor = "green"; + return getComputedStyle(temp).backgroundColor; +})(); + +// __testCSS(resisting)__. +// Creates a series of divs and CSS using media queries to set their +// background color. If all media queries match as expected, then +// all divs should have a green background color. +var testCSS = function (resisting) { + document.getElementById("display").innerHTML = generateHtmlLines(resisting); + document.getElementById("test-css").innerHTML = generateCSSLines(resisting); + let cssTestDivs = document.querySelectorAll(".spoof,.suppress"); + for (let div of cssTestDivs) { + let color = window.getComputedStyle(div).backgroundColor; + ok(color === green, "CSS for '" + div.id + "'"); + } +}; + +// __testOSXFontSmoothing(resisting)__. +// When fingerprinting resistance is enabled, the `getComputedStyle` +// should always return `undefined` for `MozOSXFontSmoothing`. +var testOSXFontSmoothing = function (resisting) { + let div = document.createElement("div"); + div.style.MozOsxFontSmoothing = "unset"; + let readBack = window.getComputedStyle(div).MozOsxFontSmoothing; + let smoothingPref = SpecialPowers.getBoolPref("layout.css.osx-font-smoothing.enabled", false); + is(readBack, resisting ? "" : (smoothingPref ? "auto" : ""), + "-moz-osx-font-smoothing"); +}; + +// __sleep(timeoutMs)__. +// Returns a promise that resolves after the given timeout. +var sleep = function (timeoutMs) { + return new Promise(function(resolve, reject) { + window.setTimeout(resolve); + }); +}; + +// __testMediaQueriesInPictureElements(resisting)__. +// Test to see if media queries are properly spoofed in picture elements +// when we are resisting fingerprinting. A generator function +// to be used with SpawnTask.js. +var testMediaQueriesInPictureElements = function* (resisting) { + let lines = ""; + for (let [key, offVal, onVal] of expected_values) { + let expected = resisting ? onVal : offVal; + if (expected) { + let query = constructQuery(key, expected); + lines += "\n"; + lines += " \n"; + lines += " " + key + "\n"; + lines += "
    \n"; + } + } + document.getElementById("pictures").innerHTML = lines; + var testImages = document.getElementsByClassName("testImage"); + yield sleep(0); + for (let testImage of testImages) { + ok(testImage.currentSrc.endsWith("/match.png"), "Media query '" + testImage.title + "' in picture should match."); + } +}; + +// __pushPref(key, value)__. +// Set a pref value asynchronously, returning a promise that resolves +// when it succeeds. +var pushPref = function (key, value) { + return new Promise(function(resolve, reject) { + SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve); + }); +}; + +// __test(isContent)__. +// Run all tests. A generator function to be used +// with SpawnTask.js. +var test = function* (isContent) { + for (prefValue of [false, true]) { + yield pushPref("privacy.resistFingerprinting", prefValue); + let resisting = prefValue && isContent; + expected_values.forEach( + function ([key, offVal, onVal]) { + testMatch(key, resisting ? onVal : offVal); + }); + testToggles(resisting); + if (OS === "WINNT") { + testWindowsSpecific(resisting, "-moz-os-version", windows_versions); + testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); + } + testCSS(resisting); + if (OS === "Darwin") { + testOSXFontSmoothing(resisting); + } + yield testMediaQueriesInPictureElements(resisting); + } +}; diff --git a/layout/style/test/chrome/bug535806-css.css b/layout/style/test/chrome/bug535806-css.css new file mode 100644 index 000000000..bda339f77 --- /dev/null +++ b/layout/style/test/chrome/bug535806-css.css @@ -0,0 +1 @@ +fooBar[fooBar] { color: green; } diff --git a/layout/style/test/chrome/bug535806-html.html b/layout/style/test/chrome/bug535806-html.html new file mode 100644 index 000000000..e4395da3f --- /dev/null +++ b/layout/style/test/chrome/bug535806-html.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/layout/style/test/chrome/bug535806-xul.xul b/layout/style/test/chrome/bug535806-xul.xul new file mode 100644 index 000000000..3d9a82b91 --- /dev/null +++ b/layout/style/test/chrome/bug535806-xul.xul @@ -0,0 +1,8 @@ + + + + + + + diff --git a/layout/style/test/chrome/chrome.ini b/layout/style/test/chrome/chrome.ini new file mode 100644 index 000000000..e34fce671 --- /dev/null +++ b/layout/style/test/chrome/chrome.ini @@ -0,0 +1,21 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + bug418986-2.js + bug535806-css.css + bug535806-html.html + bug535806-xul.xul + hover_helper.html + match.png + mismatch.png + +[test_author_specified_style.html] +[test_bug418986-2.xul] +[test_bug1157097.html] +[test_bug1160724.xul] +[test_bug535806.xul] +[test_display_mode.html] +[test_display_mode_reflow.html] +tags = fullscreen +[test_hover.html] +[test_moz_document_rules.html] diff --git a/layout/style/test/chrome/hover_empty.html b/layout/style/test/chrome/hover_empty.html new file mode 100644 index 000000000..7879e1ce9 --- /dev/null +++ b/layout/style/test/chrome/hover_empty.html @@ -0,0 +1,4 @@ + + + + diff --git a/layout/style/test/chrome/hover_helper.html b/layout/style/test/chrome/hover_helper.html new file mode 100644 index 000000000..37e50f69e --- /dev/null +++ b/layout/style/test/chrome/hover_helper.html @@ -0,0 +1,270 @@ + + + + Test for :hover + + + + + +Mozilla Bug +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/chrome/match.png b/layout/style/test/chrome/match.png new file mode 100644 index 000000000..d3f299bf5 Binary files /dev/null and b/layout/style/test/chrome/match.png differ diff --git a/layout/style/test/chrome/mismatch.png b/layout/style/test/chrome/mismatch.png new file mode 100644 index 000000000..8f9da3f00 Binary files /dev/null and b/layout/style/test/chrome/mismatch.png differ diff --git a/layout/style/test/chrome/moz_document_helper.html b/layout/style/test/chrome/moz_document_helper.html new file mode 100644 index 000000000..8b331b19e --- /dev/null +++ b/layout/style/test/chrome/moz_document_helper.html @@ -0,0 +1,2 @@ + +
    diff --git a/layout/style/test/chrome/test_author_specified_style.html b/layout/style/test/chrome/test_author_specified_style.html new file mode 100644 index 000000000..6b5e4f30e --- /dev/null +++ b/layout/style/test/chrome/test_author_specified_style.html @@ -0,0 +1,55 @@ + +Test for CSSStyleDeclaration.getAuthoredPropertyValue() + + + diff --git a/layout/style/test/chrome/test_bug1157097.html b/layout/style/test/chrome/test_bug1157097.html new file mode 100644 index 000000000..748a9eed2 --- /dev/null +++ b/layout/style/test/chrome/test_bug1157097.html @@ -0,0 +1,31 @@ + +Test for bug 1157097 + + + + +

    + diff --git a/layout/style/test/chrome/test_bug1160724.xul b/layout/style/test/chrome/test_bug1160724.xul new file mode 100644 index 000000000..8a2b48617 --- /dev/null +++ b/layout/style/test/chrome/test_bug1160724.xul @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + this.firstChild + + + + + + + diff --git a/layout/style/test/chrome/test_bug418986-2.xul b/layout/style/test/chrome/test_bug418986-2.xul new file mode 100644 index 000000000..2e5f8e687 --- /dev/null +++ b/layout/style/test/chrome/test_bug418986-2.xul @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/layout/style/test/chrome/test_bug535806.xul b/layout/style/test/chrome/test_bug535806.xul new file mode 100644 index 000000000..1c0b45c63 --- /dev/null +++ b/layout/style/test/chrome/test_bug535806.xul @@ -0,0 +1,43 @@ + + + + + + + diff --git a/layout/style/test/chrome/test_display_mode.html b/layout/style/test/chrome/test_display_mode.html new file mode 100644 index 000000000..244eefea2 --- /dev/null +++ b/layout/style/test/chrome/test_display_mode.html @@ -0,0 +1,94 @@ + + + + + + Test for Display Mode + + + + + + + + +Mozilla Bug 1104916 + +

    + +
    +
    + + diff --git a/layout/style/test/chrome/test_display_mode_reflow.html b/layout/style/test/chrome/test_display_mode_reflow.html new file mode 100644 index 000000000..23546578f --- /dev/null +++ b/layout/style/test/chrome/test_display_mode_reflow.html @@ -0,0 +1,74 @@ + + + + + + Test for Display Mode + + + + + + + + +Mozilla Bug 1256084 + +

    + +
    +
    + + diff --git a/layout/style/test/chrome/test_hover.html b/layout/style/test/chrome/test_hover.html new file mode 100644 index 000000000..192562a8b --- /dev/null +++ b/layout/style/test/chrome/test_hover.html @@ -0,0 +1,29 @@ + + + + Test for :hover + + + + + +Mozilla Bug +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/chrome/test_moz_document_rules.html b/layout/style/test/chrome/test_moz_document_rules.html new file mode 100644 index 000000000..87fba4055 --- /dev/null +++ b/layout/style/test/chrome/test_moz_document_rules.html @@ -0,0 +1,97 @@ + + + + Test for @-moz-document rules + + + + + +Mozilla Bug 398962 + +
    +
    +
    + + diff --git a/layout/style/test/css_properties_like_longhand.js b/layout/style/test/css_properties_like_longhand.js new file mode 100644 index 000000000..60b8fd583 --- /dev/null +++ b/layout/style/test/css_properties_like_longhand.js @@ -0,0 +1,3 @@ +var gShorthandPropertiesLikeLonghand = [ + { name: "overflow", prop: "overflow"}, +]; diff --git a/layout/style/test/descriptor_database.js b/layout/style/test/descriptor_database.js new file mode 100644 index 000000000..da672d03b --- /dev/null +++ b/layout/style/test/descriptor_database.js @@ -0,0 +1,72 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil; js-indent-level: 4 -*- */ +/* vim: set shiftwidth=4 tabstop=4 autoindent cindent noexpandtab: */ +/* 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/. */ + +// Each property has the following fields: +// domProp: The name of the relevant member of nsIDOM[NS]CSS2Properties +// values: Strings that are values for the descriptor and should be accepted. +// invalid_values: Things that are not values for the descriptor and +// should be rejected. + +var gCSSFontFaceDescriptors = { + "font-family": { + domProp: "fontFamily", + values: [ "\"serif\"", "\"cursive\"", "seriff", "Times New Roman", "TimesRoman", "\"Times New Roman\"" ], + /* not clear that the generics are really invalid */ + invalid_values: [ "sans-serif", "Times New Roman, serif", "'Times New Roman', serif", "cursive", "fantasy", "Times )", "Times !", "Times ! foo", "Times ! important" ] + }, + "font-stretch": { + domProp: "fontStretch", + values: [ "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" ], + invalid_values: [ "wider", "narrower", "normal ! important", "normal )" ] + }, + "font-style": { + domProp: "fontStyle", + values: [ "normal", "italic", "oblique" ], + invalid_values: [] + }, + "font-weight": { + domProp: "fontWeight", + values: [ "normal", "400", "bold", "100", "200", "300", "500", "600", "700", "800", "900" ], + invalid_values: [ "107", "399", "401", "699", "710", "bolder", "lighter" ] + }, + "src": { + domProp: null, + values: [ + "url(404.ttf)", + "url(\"404.eot\")", + "url(\'404.otf\')", + "url(404.ttf) format(\"truetype\")", + "url(404.ttf) format(\"truetype\", \"opentype\")", + "url(404.ttf) format(\"truetype\", \"opentype\"), url(\'404.eot\')", + "local(Times New Roman)", + "local(\'Times New Roman\')", + "local(\"Times New Roman\")", + "local(\"serif\")", + "url(404.ttf) format(\"truetype\", \"unknown\"), local(Times New Roman), url(\'404.eot\')", + ], + invalid_values: [ + "url(404.ttf) format(truetype)", + "url(404.ttf) format(\"truetype\" \"opentype\")", + "url(404.ttf) format(\"truetype\",)", + "local(\"Times New\" Roman)", + "local(serif)", /* is this valid? */ + "url(404.ttf) )", + "url(404.ttf) ) foo", + "url(404.ttf) ! important", + "url(404.ttf) ! hello", + ] + }, + "unicode-range": { + domProp: null, + values: [ "U+0-10FFFF", "U+3-7B3", "U+3??", "U+6A", "U+3????", "U+???", "U+302-302", "U+0-7,U+A-C", "U+100-17F,U+200-17F", "U+3??, U+500-513 ,U+612 , U+4????", "U+1FFF,U+200-27F" ], + invalid_values: [ "U+1????-2????", "U+0-7,A-C", "U+100-17F,200-17F", "U+6A!important", "U+6A)" ] + }, + "font-display": { + domProp: null, + values: [ "auto", "block", "swap", "fallback", "optional" ], + invalid_values: [ "normal", "initial" ] + } +} diff --git a/layout/style/test/display_mode_reflow_iframe.html b/layout/style/test/display_mode_reflow_iframe.html new file mode 100644 index 000000000..c05880ce7 --- /dev/null +++ b/layout/style/test/display_mode_reflow_iframe.html @@ -0,0 +1,23 @@ + + + + Display Mode Reflow inner frame + + + + + +
    +
    + + diff --git a/layout/style/test/empty.html b/layout/style/test/empty.html new file mode 100644 index 000000000..734c5a1c0 --- /dev/null +++ b/layout/style/test/empty.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layout/style/test/file_animations_async_tests.html b/layout/style/test/file_animations_async_tests.html new file mode 100644 index 000000000..9d4dfa1fe --- /dev/null +++ b/layout/style/test/file_animations_async_tests.html @@ -0,0 +1,77 @@ + + + + + Test for Bug 1086937 + + + + + + +Mozilla Bug 1086937 +
    +
    +
    + + diff --git a/layout/style/test/file_animations_effect_timing_duration.html b/layout/style/test/file_animations_effect_timing_duration.html new file mode 100644 index 000000000..545784665 --- /dev/null +++ b/layout/style/test/file_animations_effect_timing_duration.html @@ -0,0 +1,86 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_effect_timing_enddelay.html b/layout/style/test/file_animations_effect_timing_enddelay.html new file mode 100644 index 000000000..bd7c5084f --- /dev/null +++ b/layout/style/test/file_animations_effect_timing_enddelay.html @@ -0,0 +1,146 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_effect_timing_iterations.html b/layout/style/test/file_animations_effect_timing_iterations.html new file mode 100644 index 000000000..1c1c63f90 --- /dev/null +++ b/layout/style/test/file_animations_effect_timing_iterations.html @@ -0,0 +1,73 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_iterationstart.html b/layout/style/test/file_animations_iterationstart.html new file mode 100644 index 000000000..d1d8529ce --- /dev/null +++ b/layout/style/test/file_animations_iterationstart.html @@ -0,0 +1,60 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_pausing.html b/layout/style/test/file_animations_pausing.html new file mode 100644 index 000000000..ce4a639c5 --- /dev/null +++ b/layout/style/test/file_animations_pausing.html @@ -0,0 +1,85 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_playbackrate.html b/layout/style/test/file_animations_playbackrate.html new file mode 100644 index 000000000..93206594e --- /dev/null +++ b/layout/style/test/file_animations_playbackrate.html @@ -0,0 +1,102 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_styles_on_event.html b/layout/style/test/file_animations_styles_on_event.html new file mode 100644 index 000000000..b9cdb430c --- /dev/null +++ b/layout/style/test/file_animations_styles_on_event.html @@ -0,0 +1,70 @@ + + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_animations_with_disabled_properties.html b/layout/style/test/file_animations_with_disabled_properties.html new file mode 100644 index 000000000..4b69c7f60 --- /dev/null +++ b/layout/style/test/file_animations_with_disabled_properties.html @@ -0,0 +1,50 @@ + + + + + + + +
    + + diff --git a/layout/style/test/file_bug1055933_circle-xxl.png b/layout/style/test/file_bug1055933_circle-xxl.png new file mode 100644 index 000000000..3223a5690 Binary files /dev/null and b/layout/style/test/file_bug1055933_circle-xxl.png differ diff --git a/layout/style/test/file_bug1089417_iframe.html b/layout/style/test/file_bug1089417_iframe.html new file mode 100644 index 000000000..95208dbc5 --- /dev/null +++ b/layout/style/test/file_bug1089417_iframe.html @@ -0,0 +1,17 @@ + + + + Bug 1089417 + + + + + + + + diff --git a/layout/style/test/file_bug645998-1.css b/layout/style/test/file_bug645998-1.css new file mode 100644 index 000000000..328e6ed79 --- /dev/null +++ b/layout/style/test/file_bug645998-1.css @@ -0,0 +1 @@ +@import url("file_bug645998-2.css"); diff --git a/layout/style/test/file_bug645998-2.css b/layout/style/test/file_bug645998-2.css new file mode 100644 index 000000000..2d5edbe21 --- /dev/null +++ b/layout/style/test/file_bug645998-2.css @@ -0,0 +1 @@ +@import url("file_bug645998-1.css"); diff --git a/layout/style/test/file_bug829816.css b/layout/style/test/file_bug829816.css new file mode 100644 index 000000000..8f12ba6f5 Binary files /dev/null and b/layout/style/test/file_bug829816.css differ diff --git a/layout/style/test/file_font_loading_api_vframe.html b/layout/style/test/file_font_loading_api_vframe.html new file mode 100644 index 000000000..51dbbbee9 --- /dev/null +++ b/layout/style/test/file_font_loading_api_vframe.html @@ -0,0 +1,2 @@ + + diff --git a/layout/style/test/file_transitions_replacement_on_busy_frame.html b/layout/style/test/file_transitions_replacement_on_busy_frame.html new file mode 100644 index 000000000..c1678ab31 --- /dev/null +++ b/layout/style/test/file_transitions_replacement_on_busy_frame.html @@ -0,0 +1,93 @@ + + + + + + + + + +
    + + + diff --git a/layout/style/test/file_transitions_with_disabled_properties.html b/layout/style/test/file_transitions_with_disabled_properties.html new file mode 100644 index 000000000..75305f09b --- /dev/null +++ b/layout/style/test/file_transitions_with_disabled_properties.html @@ -0,0 +1,46 @@ + + + + + + + +
    + + diff --git a/layout/style/test/flexbox_layout_testcases.js b/layout/style/test/flexbox_layout_testcases.js new file mode 100644 index 000000000..719583630 --- /dev/null +++ b/layout/style/test/flexbox_layout_testcases.js @@ -0,0 +1,1398 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 sw=2 sts=2 et: */ + +/* + * This Source Code is subject to the terms of the Mozilla Public License + * version 2.0 (the "License"). You can obtain a copy of the License at + * http://mozilla.org/MPL/2.0/. + */ + +/** + * For the purposes of this test, flex items are specified as a hash with a + * hash-entry for each CSS property that is to be set. In these per-property + * entries, the key is the property-name, and the value can be either of the + * following: + * (a) the property's specified value (which indicates that we don't need to + * bother checking the computed value of this particular property) + * ...OR... + * (b) an array with 2-3 entries... + * [specifiedValue, expectedComputedValue (, epsilon) ] + * ...which indicates that the property's computed value should be + * checked. The array's first entry (for the specified value) may be + * null; this means that no value should be explicitly specified for this + * property. The second entry is the property's expected computed + * value. The third (optional) entry is an epsilon value, which allows for + * fuzzy equality when testing the computed value. + * + * To allow these testcases to be re-used in both horizontal and vertical + * flex containers, we specify "width"/"min-width"/etc. using the aliases + * "_main-size", "_min-main-size", etc. The test code can map these + * placeholder names to their corresponding property-names using the maps + * defined below -- gRowPropertyMapping, gColumnPropertyMapping, etc. + * + * If the testcase needs to customize its flex container at all (e.g. by + * specifying a custom container-size), it can do so by including a hash + * called "container_properties", with propertyName:propertyValue mappings. + * (This hash can use aliased property-names like "_main-size" as well.) + */ + +// The standard main-size we'll use for our flex container when setting up +// the testcases defined below: +var gDefaultFlexContainerSize = "200px"; + +// Left-to-right versions of placeholder property-names used in +// testcases below: +var gRowPropertyMapping = +{ + "_main-size": "width", + "_min-main-size": "min-width", + "_max-main-size": "max-width", + "_border-main-start-width": "border-left-width", + "_border-main-end-width": "border-right-width", + "_padding-main-start": "padding-left", + "_padding-main-end": "padding-right", + "_margin-main-start": "margin-left", + "_margin-main-end": "margin-right" +}; + +// Right-to-left versions of placeholder property-names used in +// testcases below: +var gRowReversePropertyMapping = +{ + "_main-size": "width", + "_min-main-size": "min-width", + "_max-main-size": "max-width", + "_border-main-start-width": "border-right-width", + "_border-main-end-width": "border-left-width", + "_padding-main-start": "padding-right", + "_padding-main-end": "padding-left", + "_margin-main-start": "margin-right", + "_margin-main-end": "margin-left" +}; + +// Top-to-bottom versions of placeholder property-names used in +// testcases below: +var gColumnPropertyMapping = +{ + "_main-size": "height", + "_min-main-size": "min-height", + "_max-main-size": "max-height", + "_border-main-start-width": "border-top-width", + "_border-main-end-width": "border-bottom-width", + "_padding-main-start": "padding-top", + "_padding-main-end": "padding-bottom", + "_margin-main-start": "margin-top", + "_margin-main-end": "margin-bottom" +}; + +// Bottom-to-top versions of placeholder property-names used in +// testcases below: +var gColumnReversePropertyMapping = +{ + "_main-size": "height", + "_min-main-size": "min-height", + "_max-main-size": "max-height", + "_border-main-start-width": "border-bottom-width", + "_border-main-end-width": "border-top-width", + "_padding-main-start": "padding-bottom", + "_padding-main-end": "padding-top", + "_margin-main-start": "margin-bottom", + "_margin-main-end": "margin-top" +}; + +// The list of actual testcase definitions: +var gFlexboxTestcases = +[ + // No flex properties specified --> should just use 'width' for sizing + { + items: + [ + { "_main-size": [ "40px", "40px" ] }, + { "_main-size": [ "65px", "65px" ] }, + ] + }, + // flex-basis is specified: + { + items: + [ + { "flex-basis": "50px", + "_main-size": [ null, "50px" ] + }, + { + "flex-basis": "20px", + "_main-size": [ null, "20px" ] + }, + ] + }, + // flex-basis is *large* -- sum of flex-basis values is > flex container size: + // (w/ 0 flex-shrink so we don't shrink): + { + items: + [ + { + "flex": "0 0 150px", + "_main-size": [ null, "150px" ] + }, + { + "flex": "0 0 90px", + "_main-size": [ null, "90px" ] + }, + ] + }, + // flex-basis is *large* -- each flex-basis value is > flex container size: + // (w/ 0 flex-shrink so we don't shrink): + { + items: + [ + { + "flex": "0 0 250px", + "_main-size": [ null, "250px" ] + }, + { + "flex": "0 0 400px", + "_main-size": [ null, "400px" ] + }, + ] + }, + // flex-basis has percentage value: + { + items: + [ + { + "flex-basis": "30%", + "_main-size": [ null, "60px" ] + }, + { + "flex-basis": "45%", + "_main-size": [ null, "90px" ] + }, + ] + }, + // flex-basis has calc(percentage) value: + { + items: + [ + { + "flex-basis": "calc(20%)", + "_main-size": [ null, "40px" ] + }, + { + "flex-basis": "calc(80%)", + "_main-size": [ null, "160px" ] + }, + ] + }, + // flex-basis has calc(percentage +/- length) value: + { + items: + [ + { + "flex-basis": "calc(10px + 20%)", + "_main-size": [ null, "50px" ] + }, + { + "flex-basis": "calc(60% - 1px)", + "_main-size": [ null, "119px" ] + }, + ] + }, + // flex-grow is specified: + { + items: + [ + { + "flex": "1", + "_main-size": [ null, "60px" ] + }, + { + "flex": "2", + "_main-size": [ null, "120px" ] + }, + { + "flex": "0 20px", + "_main-size": [ null, "20px" ] + } + ] + }, + // Same ratio as prev. testcase; making sure we handle float inaccuracy + { + items: + [ + { + "flex": "100000", + "_main-size": [ null, "60px" ] + }, + { + "flex": "200000", + "_main-size": [ null, "120px" ] + }, + { + "flex": "0.000001 20px", + "_main-size": [ null, "20px" ] + } + ] + }, + // Same ratio as prev. testcase, but with items cycled and w/ + // "flex: none" & explicit size instead of "flex: 0 20px" + { + items: + [ + { + "flex": "none", + "_main-size": [ "20px", "20px" ] + }, + { + "flex": "1", + "_main-size": [ null, "60px" ] + }, + { + "flex": "2", + "_main-size": [ null, "120px" ] + } + ] + }, + + // ...and now with flex-grow:[huge] to be sure we handle infinite float values + // gracefully. + { + items: + [ + { + "flex": "9999999999999999999999999999999999999999999999999999999", + "_main-size": [ null, "200px" ] + }, + ] + }, + { + items: + [ + { + "flex": "9999999999999999999999999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "9999999999999999999999999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "9999999999999999999999999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "9999999999999999999999999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + ] + }, + { + items: + [ + { + "flex": "99999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "99999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "99999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + { + "flex": "99999999999999999999999999999999999", + "_main-size": [ null, "50px" ] + }, + ] + }, + + // And now, some testcases to check that we handle float accumulation error + // gracefully. + + // First, a testcase with just a custom-sized huge container, to be sure we'll + // be able to handle content on that scale, in the subsequent more-complex + // testcases: + { + container_properties: + { + "_main-size": "9000000px" + }, + items: + [ + { + "flex": "1", + "_main-size": [ null, "9000000px" ] + }, + ] + }, + // ...and now with two flex items dividing up that container's huge size: + { + container_properties: + { + "_main-size": "9000000px" + }, + items: + [ + { + "flex": "2", + "_main-size": [ null, "6000000px" ] + }, + { + "flex": "1", + "_main-size": [ null, "3000000px" ] + }, + ] + }, + + // OK, now to actually test accumulation error. Below, we have six flex items + // splitting up the container's size, with huge differences between flex + // weights. For simplicity, I've set up the weights so that they sum exactly + // to the container's size in px. So 1 unit of flex *should* get you 1px. + // + // NOTE: The expected computed "_main-size" values for the flex items below + // appear to add up to more than their container's size, which would suggest + // that they overflow their container unnecessarily. But they don't actually + // overflow -- this discrepancy is simply because Gecko's code for reporting + // computed-sizes rounds to 6 significant figures (in particular, the method + // (nsTSubstring_CharT::AppendFloat() does this). Internally, in app-units, + // the child frames' main-sizes add up exactly to the container's main-size, + // as you'd hope & expect. + { + container_properties: + { + "_main-size": "9000000px" + }, + items: + [ + { + "flex": "3000000", + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "1", + "_main-size": [ null, "1px" ] + }, + { + "flex": "1", + "_main-size": [ null, "1px" ] + }, + { + "flex": "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "1", + "_main-size": [ null, "1px", 0.2 ] + }, + ] + }, + // Same flex items as previous testcase, but now reordered such that the items + // with tiny flex weights are all listed last: + { + container_properties: + { + "_main-size": "9000000px" + }, + items: + [ + { + "flex": "3000000", + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "1", + "_main-size": [ null, "1px", 0.2 ] + }, + { + "flex": "1", + "_main-size": [ null, "1px", 0.2 ] + }, + { + "flex": "1", + "_main-size": [ null, "1px", 0.2 ] + }, + ] + }, + // Same flex items as previous testcase, but now reordered such that the items + // with tiny flex weights are all listed first: + { + container_properties: + { + "_main-size": "9000000px" + }, + items: + [ + { + "flex": "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [ null, "1px", 0.2 ] + }, + { + "flex": "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [ null, "1px", 0.2 ] + }, + { + "flex": "1", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths: + "_main-size": [ null, "1px", 0.2 ] + }, + { + "flex": "3000000", + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "2999999", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + { + "flex": "2999998", + // NOTE: Expected value is off slightly, from float error when + // resolving flexible lengths & when generating computed value string: + "_main-size": [ null, "3000000px" ] + }, + ] + }, + + // Trying "flex: auto" (== "1 1 auto") w/ a mix of flex-grow/flex-basis values + { + items: + [ + { + "flex": "auto", + "_main-size": [ null, "45px" ] + }, + { + "flex": "2", + "_main-size": [ null, "90px" ] + }, + { + "flex": "20px 1 0", + "_main-size": [ null, "65px" ] + } + ] + }, + // Same as previous, but with items cycled & different syntax + { + items: + [ + { + "flex": "20px", + "_main-size": [ null, "65px" ] + }, + { + "flex": "1", + "_main-size": [ null, "45px" ] + }, + { + "flex": "2", + "_main-size": [ null, "90px" ] + } + ] + }, + { + items: + [ + { + "flex": "2", + "_main-size": [ null, "100px" ], + "border": "0px dashed", + "_border-main-start-width": [ "5px", "5px" ], + "_border-main-end-width": [ "15px", "15px" ], + "_margin-main-start": [ "22px", "22px" ], + "_margin-main-end": [ "8px", "8px" ] + }, + { + "flex": "1", + "_main-size": [ null, "50px" ], + "_margin-main-start": [ "auto", "0px" ], + "_padding-main-end": [ "auto", "0px" ], + } + ] + }, + // Test negative flexibility: + + // Basic testcase: just 1 item (relying on initial "flex-shrink: 1") -- + // should shrink to container size. + { + items: + [ + { "_main-size": [ "400px", "200px" ] }, + ], + }, + // ...and now with a "flex" specification and a different flex-shrink value: + { + items: + [ + { + "flex": "4 2 250px", + "_main-size": [ null, "200px" ] + }, + ], + }, + // ...and now with multiple items, which all shrink proportionally (by 50%) + // to fit to the container, since they have the same (initial) flex-shrink val + { + items: + [ + { "_main-size": [ "80px", "40px" ] }, + { "_main-size": [ "40px", "20px" ] }, + { "_main-size": [ "30px", "15px" ] }, + { "_main-size": [ "250px", "125px" ] }, + ] + }, + // ...and now with positive flexibility specified. (should have no effect, so + // everything still shrinks by the same proportion, since the flex-shrink + // values are all the same). + { + items: + [ + { + "flex": "4 3 100px", + "_main-size": [ null, "80px" ] + }, + { + "flex": "5 3 50px", + "_main-size": [ null, "40px" ] + }, + { + "flex": "0 3 100px", + "_main-size": [ null, "80px" ] + } + ] + }, + // ...and now with *different* flex-shrink values: + { + items: + [ + { + "flex": "4 2 50px", + "_main-size": [ null, "30px" ] + }, + { + "flex": "5 3 50px", + "_main-size": [ null, "20px" ] + }, + { + "flex": "0 0 150px", + "_main-size": [ null, "150px" ] + } + ] + }, + // Same ratio as prev. testcase; making sure we handle float inaccuracy + { + items: + [ + { + "flex": "4 20000000 50px", + "_main-size": [ null, "30px" ] + }, + { + "flex": "5 30000000 50px", + "_main-size": [ null, "20px" ] + }, + { + "flex": "0 0.0000001 150px", + "_main-size": [ null, "150px" ] + } + ] + }, + // Another "different flex-shrink values" testcase: + { + items: + [ + { + "flex": "4 2 115px", + "_main-size": [ null, "69px" ] + }, + { + "flex": "5 1 150px", + "_main-size": [ null, "120px" ] + }, + { + "flex": "1 4 30px", + "_main-size": [ null, "6px" ] + }, + { + "flex": "1 0 5px", + "_main-size": [ null, "5px" ] + }, + ] + }, + + // ...and now with min-size (clamping the effects of flex-shrink on one item): + { + items: + [ + { + "flex": "4 5 75px", + "_min-main-size": "50px", + "_main-size": [ null, "50px" ], + }, + { + "flex": "5 5 100px", + "_main-size": [ null, "62.5px" ] + }, + { + "flex": "0 4 125px", + "_main-size": [ null, "87.5px" ] + } + ] + }, + + // Test a min-size that's much larger than initial preferred size, but small + // enough that our flexed size pushes us over it: + { + items: + [ + { + "flex": "auto", + "_min-main-size": "110px", + "_main-size": [ "50px", "125px" ] + }, + { + "flex": "auto", + "_main-size": [ null, "75px" ] + } + ] + }, + + // Test a min-size that's much larger than initial preferred size, and is + // even larger than our positively-flexed size, so that we have to increase it + // (as a 'min violation') after we've flexed. + { + items: + [ + { + "flex": "auto", + "_min-main-size": "150px", + "_main-size": [ "50px", "150px" ] + }, + { + "flex": "auto", + "_main-size": [ null, "50px" ] + } + ] + }, + + // Test min-size on multiple items simultaneously: + { + items: + [ + { + "flex": "auto", + "_min-main-size": "20px", + "_main-size": [ null, "20px" ] + }, + { + "flex": "9 auto", + "_min-main-size": "150px", + "_main-size": [ "50px", "180px" ] + }, + ] + }, + { + items: + [ + { + "flex": "1 1 0px", + "_min-main-size": "90px", + "_main-size": [ null, "90px" ] + }, + { + "flex": "1 1 0px", + "_min-main-size": "80px", + "_main-size": [ null, "80px" ] + }, + { + "flex": "1 1 40px", + "_main-size": [ null, "30px" ] + } + ] + }, + + // Test a case where _min-main-size will be violated on different items in + // successive iterations of the "resolve the flexible lengths" loop + { + items: + [ + { + "flex": "1 2 100px", + "_min-main-size": "90px", + "_main-size": [ null, "90px" ] + }, + { + "flex": "1 1 100px", + "_min-main-size": "70px", + "_main-size": [ null, "70px" ] + }, + { + "flex": "1 1 100px", + "_main-size": [ null, "40px" ] + } + ] + }, + + // Test some cases that have a min-size violation on one item and a + // max-size violation on another: + + // Here, both items initially grow to 100px. That violates both + // items' sizing constraints (it's smaller than the min-size and larger than + // the max-size), so we clamp both of them and sum the clamping-differences: + // + // (130px - 100px) + (50px - 100px) = (30px) + (-50px) = -20px + // + // This sum is negative, so (per spec) we freeze the item that had its + // max-size violated (the second one) and restart the algorithm. This time, + // all the available space (200px - 50px = 150px) goes to the not-yet-frozen + // first item, and that puts it above its min-size, so all is well. + { + items: + [ + { + "flex": "auto", + "_min-main-size": "130px", + "_main-size": [ null, "150px" ] + }, + { + "flex": "auto", + "_max-main-size": "50px", + "_main-size": [ null, "50px" ] + }, + ] + }, + + // As above, both items initially grow to 100px, and that violates both items' + // constraints. However, now the sum of the clamping differences is: + // + // (130px - 100px) + (80px - 100px) = (30px) + (-20px) = 10px + // + // This sum is positive, so (per spec) we freeze the item that had its + // min-size violated (the first one) and restart the algorithm. This time, + // all the available space (200px - 130px = 70px) goes to the not-yet-frozen + // second item, and that puts it below its max-size, so all is well. + { + items: + [ + { + "flex": "auto", + "_min-main-size": "130px", + "_main-size": [ null, "130px" ] + }, + { + "flex": "auto", + "_max-main-size": "80px", + "_main-size": [ null, "70px" ] + }, + ] + }, + + // As above, both items initially grow to 100px, and that violates both items' + // constraints. So we clamp both items and sum the clamping differences to + // see what to do next. The sum is: + // + // (80px - 100px) + (120px - 100px) = (-20px) + (20px) = 0px + // + // Per spec, if the sum is 0, we're done -- we leave both items at their + // clamped sizes. + { + items: + [ + { + "flex": "auto", + "_max-main-size": "80px", + "_main-size": [ null, "80px" ] + }, + { + "flex": "auto", + "_min-main-size": "120px", + "_main-size": [ null, "120px" ] + }, + ] + }, + + // Test cases where flex-grow sums to less than 1: + // =============================================== + // This makes us treat the flexibilities like "fraction of free space" + // instead of weights, so that e.g. a single item with "flex-grow: 0.1" + // will only get 10% of the free space instead of all of the free space. + + // Basic cases where flex-grow sum is less than 1: + { + items: + [ + { + "flex": "0.1 100px", + "_main-size": [ null, "110px" ] // +10% of free space + }, + ] + }, + { + items: + [ + { + "flex": "0.8 0px", + "_main-size": [ null, "160px" ] // +80% of free space + }, + ] + }, + + // ... and now with two flex items: + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "110px" ] // +40% of free space + }, + { + "flex": "0.2 30px", + "_main-size": [ null, "50px" ] // +20% of free space + }, + ] + }, + + // ...and now with max-size modifying how much free space one item can take: + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "110px" ] // +40% of free space + }, + { + "flex": "0.2 30px", + "_max-main-size": "35px", + "_main-size": [ null, "35px" ] // +20% free space, then clamped + }, + ] + }, + // ...and now with a max-size smaller than our flex-basis: + // (This makes us freeze the second item right away, before we compute + // the initial free space.) + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "118px" ] // +40% of 200px-70px-10px + }, + { + "flex": "0.2 30px", + "_max-main-size": "10px", + "_main-size": [ null, "10px" ] // immediately frozen + }, + ] + }, + // ...and now with a max-size and a huge flex-basis, such that we initially + // have negative free space, which makes the "% of [original] free space" + // calculations a bit more subtle. We set the "original free space" after + // we've clamped the second item (the first time the free space is positive). + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "118px" ] // +40% of free space _after freezing + // the other item_ + }, + { + "flex": "0.2 150px", + "_max-main-size": "10px", + "_main-size": [ null, "10px" ] // clamped immediately + }, + ] + }, + + // Now with min-size modifying how much free space our items take: + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "110px" ] // +40% of free space + }, + { + "flex": "0.2 30px", + "_min-main-size": "70px", + "_main-size": [ null, "70px" ] // +20% free space, then clamped + }, + ] + }, + + // ...and now with a large enough min-size that it prevents the other flex + // item from taking its full desired portion of the original free space: + { + items: + [ + { + "flex": "0.4 70px", + "_main-size": [ null, "80px" ] // (Can't take my full +40% of + // free space due to other item's + // large min-size.) + }, + { + "flex": "0.2 30px", + "_min-main-size": "120px", + "_main-size": [ null, "120px" ] // +20% free space, then clamped + }, + ] + }, + // ...and now with a large-enough min-size that it pushes the other flex item + // to actually shrink a bit (with default "flex-shrink:1"): + { + items: + [ + { + "flex": "0.3 30px", + "_main-size": [ null, "20px" ] // -10px, instead of desired +45px + }, + { + "flex": "0.2 20px", + "_min-main-size": "180px", + "_main-size": [ null, "180px" ] // +160px, instead of desired +30px + }, + ] + }, + + // In this case, the items' flexibilities don't initially sum to < 1, but they + // do after we freeze the third item for violating its max-size. + { + items: + [ + { + "flex": "0.3 30px", + "_main-size": [ null, "75px" ] + // 1st loop: desires (0.3 / 5) * 150px = 9px. Tentatively granted. + // 2nd loop: desires 0.3 * 150px = 45px. Tentatively granted. + // 3rd loop: desires 0.3 * 150px = 45px. Granted +45px. + }, + { + "flex": "0.2 20px", + "_max-main-size": "30px", + "_main-size": [ null, "30px" ] + // First loop: desires (0.2 / 5) * 150px = 6px. Tentatively granted. + // Second loop: desires 0.2 * 150px = 30px. Frozen at +10px. + }, + { + "flex": "4.5 0px", + "_max-main-size": "20px", + "_main-size": [ null, "20px" ] + // First loop: desires (4.5 / 5) * 150px = 135px. Frozen at +20px. + }, + ] + }, + + // Make sure we calculate "original free space" correctly when one of our + // flex items will be clamped right away, due to max-size preventing it from + // growing at all: + { + // Here, the second flex item is effectively inflexible; it's + // immediately frozen at 40px since we're growing & this item's max size + // trivially prevents it from growing. This leaves us with an "original + // free space" of 60px. The first flex item takes half of that, due to + // its flex-grow value of 0.5. + items: + [ + { + "flex": "0.5 100px", + "_main-size": [ null, "130px" ] + }, + { + "flex": "1 98px", + "_max-main-size": "40px", + "_main-size": [ null, "40px" ] + }, + ] + }, + { + // Same as previous example, but with a larger flex-basis on the second + // element (which shouldn't ultimately matter, because its max size clamps + // its size immediately anyway). + items: + [ + { + "flex": "0.5 100px", + "_main-size": [ null, "130px" ] + }, + { + "flex": "1 101px", + "_max-main-size": "40px", + "_main-size": [ null, "40px" ] + }, + ] + }, + + { + // Here, the third flex item is effectively inflexible; it's immediately + // frozen at 0px since we're growing & this item's max size trivially + // prevents it from growing. This leaves us with an "original free space" of + // 100px. The first flex item takes 40px, and the third takes 50px, due to + // their flex values of 0.4 and 0.5. + items: + [ + { + "flex": "0.4 50px", + "_main-size": [ null, "90px" ] + }, + { + "flex": "0.5 50px", + "_main-size": [ null, "100px" ] + }, + { + "flex": "0 90px", + "_max-main-size": "0px", + "_main-size": [ null, "0px" ] + }, + ] + }, + { + // Same as previous example, but with slightly larger flex-grow values on + // the first and second items, which sum to 1.0 and produce slightly larger + // main sizes. This demonstrates that there's no discontinuity between the + // "< 1.0 sum" to ">= 1.0 sum" behavior, in this situation at least. + items: + [ + { + "flex": "0.45 50px", + "_main-size": [ null, "95px" ] + }, + { + "flex": "0.55 50px", + "_main-size": [ null, "105px" ] + }, + { + "flex": "0 90px", + "_max-main-size": "0px", + "_main-size": [ null, "0px" ] + }, + ] + }, + + // Test cases where flex-shrink sums to less than 1: + // ================================================= + // This makes us treat the flexibilities more like "fraction of (negative) + // free space" instead of weights, so that e.g. a single item with + // "flex-shrink: 0.1" will only shrink by 10% of amount that it overflows + // its container by. + // + // It gets a bit more complex when there are multiple flex items, because + // flex-shrink is scaled by the flex-basis before it's used as a weight. But + // even with that scaling, the general principal is that e.g. if the + // flex-shrink values *sum* to 0.6, then the items will collectively only + // shrink by 60% (and hence will still overflow). + + // Basic cases where flex-grow sum is less than 1: + { + items: + [ + { + "flex": "0 0.1 300px", + "_main-size": [ null, "290px" ] // +10% of (negative) free space + }, + ] + }, + { + items: + [ + { + "flex": "0 0.8 400px", + "_main-size": [ null, "240px" ] // +80% of (negative) free space + }, + ] + }, + + // ...now with two flex items, with the same flex-basis value: + { + items: + [ + { + "flex": "0 0.4 150px", + "_main-size": [ null, "110px" ] // +40% of (negative) free space + }, + { + "flex": "0 0.2 150px", + "_main-size": [ null, "130px" ] // +20% of (negative) free space + }, + ] + }, + + // ...now with two flex items, with different flex-basis values (and hence + // differently-scaled flex factors): + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "76px" ] + }, + { + "flex": "0 0.1 200px", + "_main-size": [ null, "184px" ] + } + ] + // Notes: + // - Free space: -100px + // - Sum of flex-shrink factors: 0.3 + 0.1 = 0.4 + // - Since that sum ^ is < 1, we'll only distribute that fraction of + // the free space. We'll distribute: -100px * 0.4 = -40px + // + // - 1st item's scaled flex factor: 0.3 * 100px = 30 + // - 2nd item's scaled flex factor: 0.1 * 200px = 20 + // - 1st item's share of distributed free space: 30/(30+20) = 60% + // - 2nd item's share of distributed free space: 20/(30+20) = 40% + // + // SO: + // - 1st item gets 60% * -40px = -24px. 100px-24px = 76px + // - 2nd item gets 40% * -40px = -16px. 200px-16px = 184px + }, + + // ...now with min-size modifying how much one item can shrink: + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "70px" ] + }, + { + "flex": "0 0.1 200px", + "_min-main-size": "190px", + "_main-size": [ null, "190px" ] + } + ] + // Notes: + // - We proceed as in previous testcase, but clamp the second flex item + // at its min main size. + // - After that point, we have a total flex-shrink of = 0.3, so we + // distribute 0.3 * -100px = -30px to the remaining unfrozen flex + // items. Since there's only one unfrozen item left, it gets all of it. + }, + + // ...now with min-size larger than our flex-basis: + // (This makes us freeze the second item right away, before we compute + // the initial free space.) + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "55px" ] // +30% of 200px-100px-250px + }, + { + "flex": "0 0.1 200px", + "_min-main-size": "250px", + "_main-size": [ null, "250px" ] // immediately frozen + } + ] + // (Same as previous example, except the min-main-size prevents the + // second item from shrinking at all) + }, + + // ...and now with a min-size and a small flex-basis, such that we initially + // have positive free space, which makes the "% of [original] free space" + // calculations a bit more subtle. We set the "original free space" after + // we've clamped the second item (the first time the free space is negative). + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "70px" ] + }, + { + "flex": "0 0.1 50px", + "_min-main-size": "200px", + "_main-size": [ null, "200px" ] + } + ] + }, + + // Now with max-size making an item shrink more than its flex-shrink value + // calls for: + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "70px" ] + }, + { + "flex": "0 0.1 200px", + "_max-main-size": "150px", + "_main-size": [ null, "150px" ] + } + ] + // Notes: + // - We proceed as in an earlier testcase, but clamp the second flex item + // at its max main size. + // - After that point, we have a total flex-shrink of = 0.3, so we + // distribute 0.3 * -100px = -30px to the remaining unfrozen flex + // items. Since there's only one unfrozen item left, it gets all of it. + }, + + // ...and now with a small enough max-size that it prevents the other flex + // item from taking its full desired portion of the (negative) original free + // space: + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "90px" ] + }, + { + "flex": "0 0.1 200px", + "_max-main-size": "110px", + "_main-size": [ null, "110px" ] + } + ] + // Notes: + // - We proceed as in an earlier testcase, but clamp the second flex item + // at its max main size. + // - After that point, we have a total flex-shrink of 0.3, which would + // have us distribute 0.3 * -100px = -30px to the (one) remaining + // unfrozen flex item. But our remaining free space is only -10px at + // that point, so we distribute that instead. + }, + + // ...and now with a small enough max-size that it pushes the other flex item + // to actually grow a bit (with custom "flex-grow: 1" for this testcase): + { + items: + [ + { + "flex": "1 0.3 100px", + "_main-size": [ null, "120px" ] + }, + { + "flex": "1 0.1 200px", + "_max-main-size": "80px", + "_main-size": [ null, "80px" ] + } + ] + }, + + // In this case, the items' flexibilities don't initially sum to < 1, but they + // do after we freeze the third item for violating its min-size. + { + items: + [ + { + "flex": "0 0.3 100px", + "_main-size": [ null, "76px" ] + }, + { + "flex": "0 0.1 150px", + "_main-size": [ null, "138px" ] + }, + { + "flex": "0 0.8 10px", + "_min-main-size": "40px", + "_main-size": [ null, "40px" ] + } + ] + // Notes: + // - We immediately freeze the 3rd item, since we're shrinking and its + // min size obviously prevents it from shrinking at all. This leaves + // 200px - 100px - 150px - 40px = -90px of "initial free space". + // + // - Our remaining flexible items have a total flex-shrink of 0.4, + // so we can distribute a total of 0.4 * -90px = -36px + // + // - We distribute that space using *scaled* flex factors: + // * 1st item's scaled flex factor: 0.3 * 100px = 30 + // * 2nd item's scaled flex factor: 0.1 * 150px = 15 + // ...which means... + // * 1st item's share of distributed free space: 30/(30+15) = 2/3 + // * 2nd item's share of distributed free space: 15/(30+15) = 1/3 + // + // SO: + // - 1st item gets 2/3 * -36px = -24px. 100px - 24px = 76px + // - 2nd item gets 1/3 * -36px = -12px. 150px - 12px = 138px + }, + + // In this case, the items' flexibilities sum to > 1, in part due to an item + // that *can't actually shrink* due to its 0 flex-basis (which gives it a + // "scaled flex factor" of 0). This prevents us from triggering the special + // behavior for flexibilities that sum to less than 1, and as a result, the + // first item ends up absorbing all of the free space. + { + items: + [ + { + "flex": "0 .5 300px", + "_main-size": [ null, "200px" ] + }, + { + "flex": "0 5 0px", + "_main-size": [ null, "0px" ] + } + ] + }, + + // This case is similar to the one above, but with a *barely* nonzero base + // size for the second item. This should produce a result similar to the case + // above. (In particular, we should first distribute a very small amount of + // negative free space to the second item, getting it to approximately zero, + // and distribute the bulk of the negative free space to the first item, + // getting it to approximately 200px.) + { + items: + [ + { + "flex": "0 .5 300px", + "_main-size": [ null, "200px" ] + }, + { + "flex": "0 1 0.01px", + "_main-size": [ null, "0px" ] + } + ] + }, + // This case is similar to the ones above, but now we've increased the + // flex-shrink value on the second-item so that it claims enough of the + // negative free space to go below its min-size (0px). So, it triggers a min + // violation & is frozen. For the loop *after* the min violation, the sum of + // the remaining flex items' flex-shrink values is less than 1, so we trigger + // the special <1 behavior and only distribute half of the remaining + // (negative) free space to the first item (instead of all of it). + { + items: + [ + { + "flex": "0 .5 300px", + "_main-size": [ null, "250px" ] + }, + { + "flex": "0 5 0.01px", + "_main-size": [ null, "0px" ] + } + ] + }, +]; diff --git a/layout/style/test/gen-css-properties.py b/layout/style/test/gen-css-properties.py new file mode 100644 index 000000000..7196c5e71 --- /dev/null +++ b/layout/style/test/gen-css-properties.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/. + +from __future__ import print_function + +import os +import sys +import subprocess + +def main(output, css_properties, exe): + # moz.build passes in the exe name without any path, so to run it we need to + # prepend the './' + run_exe = exe if os.path.isabs(exe) else './%s' % exe + + # Use universal_newlines so everything is '\n', which gets converted to + # '\r\n' when writing out the file in Windows. + data = subprocess.check_output([run_exe], universal_newlines=True) + with open(css_properties) as f: + data += f.read() + output.write(data) + +if __name__ == '__main__': + main(sys.stdout, *sys.argv[1:]) diff --git a/layout/style/test/media_queries_dynamic_xbl_binding.xml b/layout/style/test/media_queries_dynamic_xbl_binding.xml new file mode 100644 index 000000000..4b8515539 --- /dev/null +++ b/layout/style/test/media_queries_dynamic_xbl_binding.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/layout/style/test/media_queries_dynamic_xbl_iframe.html b/layout/style/test/media_queries_dynamic_xbl_iframe.html new file mode 100644 index 000000000..50e8008fa --- /dev/null +++ b/layout/style/test/media_queries_dynamic_xbl_iframe.html @@ -0,0 +1,5 @@ + + +

    Hello

    diff --git a/layout/style/test/media_queries_dynamic_xbl_style.css b/layout/style/test/media_queries_dynamic_xbl_style.css new file mode 100644 index 000000000..5c99c07ee --- /dev/null +++ b/layout/style/test/media_queries_dynamic_xbl_style.css @@ -0,0 +1,6 @@ +@media (orientation: portrait) { + div { color: purple; } +} +@media (orientation: landscape) { + div { color: blue; } +} diff --git a/layout/style/test/media_queries_iframe.html b/layout/style/test/media_queries_iframe.html new file mode 100644 index 000000000..141ecdcd9 --- /dev/null +++ b/layout/style/test/media_queries_iframe.html @@ -0,0 +1,15 @@ + + + + Media Queries Test inner frame + + + + + + + + diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini new file mode 100644 index 000000000..406c6f901 --- /dev/null +++ b/layout/style/test/mochitest.ini @@ -0,0 +1,312 @@ +[DEFAULT] +support-files = + animation_utils.js + ccd-quirks.html + ccd.sjs + ccd-standards.html + chrome/bug418986-2.js + chrome/match.png + chrome/mismatch.png + descriptor_database.js + display_mode_reflow_iframe.html + empty.html + media_queries_dynamic_xbl_binding.xml + media_queries_dynamic_xbl_iframe.html + media_queries_dynamic_xbl_style.css + media_queries_iframe.html + neverending_font_load.sjs + neverending_stylesheet_load.sjs + post-redirect-1.css + post-redirect-2.css + post-redirect-3.css + property_database.js + redirect.sjs + style_attribute_tests.js + unstyled.css + unstyled-frame.css + unstyled-frame.xml + unstyled.xml + viewport_units_iframe.html + visited_image_loading_frame_empty.html + visited_image_loading_frame.html + visited_image_loading.sjs + visited-lying-inner.html + visited-pref-iframe.html + xbl_bindings.xml + +[test_acid3_test46.html] +[test_addSheet.html] +support-files = additional_sheets_helper.html +[test_additional_sheets.html] +support-files = additional_sheets_helper.html +[test_all_shorthand.html] +[test_animations.html] +skip-if = toolkit == 'android' +[test_animations_async_tests.html] +support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html +[test_animations_dynamic_changes.html] +[test_animations_effect_timing_duration.html] +support-files = file_animations_effect_timing_duration.html +[test_animations_effect_timing_enddelay.html] +support-files = file_animations_effect_timing_enddelay.html +[test_animations_effect_timing_iterations.html] +support-files = file_animations_effect_timing_iterations.html +[test_animations_event_order.html] +[test_animations_event_handler_attribute.html] +[test_animations_iterationstart.html] +support-files = file_animations_iterationstart.html +[test_animations_omta.html] +[test_animations_omta_start.html] +[test_animations_pausing.html] +support-files = file_animations_pausing.html +[test_animations_playbackrate.html] +support-files = file_animations_playbackrate.html +[test_animations_styles_on_event.html] +support-files = file_animations_styles_on_event.html +[test_animations_with_disabled_properties.html] +support-files = file_animations_with_disabled_properties.html +[test_any_dynamic.html] +[test_at_rule_parse_serialize.html] +[test_attribute_selector_eof_behavior.html] +[test_background_blend_mode.html] +[test_box_size_keywords.html] +[test_bug73586.html] +[test_bug74880.html] +[test_bug98997.html] +[test_bug160403.html] +[test_bug200089.html] +[test_bug221428.html] +[test_bug229915.html] +[test_bug302186.html] +[test_bug319381.html] +[test_bug357614.html] +[test_bug363146.html] +[test_bug372770.html] +[test_bug373293.html] +[test_bug377947.html] +[test_bug379440.html] +skip-if = toolkit == 'android' +[test_bug379741.html] +[test_bug382027.html] +[test_bug383075.html] +[test_bug387615.html] +[test_bug389464.html] +[test_bug391034.html] +[test_bug391221.html] +[test_bug397427.html] +[test_bug399349.html] +[test_bug401046.html] +skip-if = true # Bug 701060 +[test_bug405818.html] +[test_bug412901.html] +skip-if = android_version == '18' # bug 1147986 +[test_bug413958.html] +[test_bug418986-2.html] +[test_bug437915.html] +[test_bug450191.html] +[test_bug453896_deck.html] +support-files = bug453896_iframe.html +[test_bug470769.html] +[test_bug499655.html] +[test_bug499655.xhtml] +[test_bug511909.html] +[test_bug517224.html] +support-files = bug517224.sjs +[test_bug524175.html] +[test_bug525952.html] +[test_bug534804.html] +[test_bug573255.html] +[test_bug580685.html] +[test_bug621351.html] +[test_bug635286.html] +[test_bug652486.html] +[test_bug657143.html] +[test_bug664955.html] +[test_bug667520.html] +[test_bug645998.html] +support-files = file_bug645998-1.css file_bug645998-2.css +[test_bug716226.html] +[test_bug732153.html] +[test_bug732209.html] +support-files = bug732209-css.sjs +[test_bug765590.html] +[test_bug771043.html] +[test_bug795520.html] +[test_bug798567.html] +[test_bug798843_pref.html] +[test_bug829816.html] +[test_bug874919.html] +support-files = file_bug829816.css +[test_bug887741_at-rules_in_declaration_lists.html] +[test_bug892929.html] +[test_bug1055933.html] +support-files = file_bug1055933_circle-xxl.png +[test_bug1089417.html] +support-files = file_bug1089417_iframe.html +[test_bug1112014.html] +[test_bug1203766.html] +[test_bug1232829.html] +[test_bug1292447.html] +[test_cascade.html] +[test_ch_ex_no_infloops.html] +[test_change_hint_optimizations.html] +[test_clip-path_polygon.html] +[test_compute_data_with_start_struct.html] +skip-if = toolkit == 'android' +[test_computed_style.html] +[test_computed_style_min_size_auto.html] +[test_computed_style_no_pseudo.html] +[test_computed_style_prefs.html] +[test_condition_text.html] +[test_condition_text_assignment.html] +[test_contain_formatting_context.html] +[test_counter_descriptor_storage.html] +[test_counter_style.html] +[test_css_cross_domain.html] +skip-if = toolkit == 'android' #bug 536603 +[test_css_eof_handling.html] +[test_css_escape_api.html] +[test_css_function_mismatched_parenthesis.html] +[test_css_loader_crossorigin_data_url.html] +[test_css_supports.html] +[test_css_supports_variables.html] +[test_default_bidi_css.html] +[test_default_computed_style.html] +[test_descriptor_storage.html] +[test_descriptor_syntax_errors.html] +[test_dont_use_document_colors.html] +[test_dynamic_change_causing_reflow.html] +[test_exposed_prop_accessors.html] +[test_extra_inherit_initial.html] +[test_align_justify_computed_values.html] +[test_flexbox_child_display_values.xhtml] +[test_flexbox_flex_grow_and_shrink.html] +[test_flexbox_flex_shorthand.html] +[test_flexbox_layout.html] +support-files = flexbox_layout_testcases.js +[test_flexbox_order.html] +[test_flexbox_order_abspos.html] +[test_flexbox_order_table.html] +[test_flexbox_reflow_counts.html] +[test_font_face_parser.html] +[test_font_family_parsing.html] +[test_font_feature_values_parsing.html] +[test_font_loading_api.html] +support-files = + BitPattern.woff + file_font_loading_api_vframe.html +[test_garbage_at_end_of_declarations.html] +[test_grid_container_shorthands.html] +[test_grid_item_shorthands.html] +[test_grid_shorthand_serialization.html] +[test_grid_computed_values.html] +[test_group_insertRule.html] +[test_hover_quirk.html] +[test_html_attribute_computed_values.html] +[test_ident_escaping.html] +[test_inherit_computation.html] +skip-if = toolkit == 'android' +[test_inherit_storage.html] +[test_initial_computation.html] +skip-if = toolkit == 'android' +[test_initial_storage.html] +[test_keyframes_rules.html] +[test_load_events_on_stylesheets.html] +[test_logical_properties.html] +[test_media_queries.html] +skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aws only; bug 1030419 +[test_media_queries_dynamic.html] +[test_media_queries_dynamic_xbl.html] +[test_media_query_list.html] +[test_moz_device_pixel_ratio.html] +[test_namespace_rule.html] +[test_of_type_selectors.xhtml] +[test_page_parser.html] +[test_parse_eof.html] +[test_parse_ident.html] +[test_parse_rule.html] +[test_parse_url.html] +[test_parser_diagnostics_unprintables.html] +[test_pixel_lengths.html] +[test_pointer-events.html] +[test_position_float_display.html] +[test_position_sticky.html] +[test_priority_preservation.html] +[test_property_database.html] +[test_property_syntax_errors.html] +[test_pseudoelement_state.html] +[test_pseudoelement_parsing.html] +[test_redundant_font_download.html] +support-files = redundant_font_download.sjs +[test_rem_unit.html] +[test_restyles_in_smil_animation.html] +[test_root_node_display.html] +[test_rule_insertion.html] +[test_rule_serialization.html] +[test_rules_out_of_sheets.html] +[test_selectors.html] +skip-if = toolkit == 'android' #bug 775227 +[test_selectors_on_anonymous_content.html] +[test_setPropertyWithNull.html] +[test_shorthand_property_getters.html] +[test_specified_value_serialization.html] +[test_style_attribute_quirks.html] +[test_style_attribute_standards.html] +[test_style_struct_copy_constructors.html] +[test_supports_rules.html] +[test_system_font_serialization.html] +[test_text_decoration_shorthands.html] +[test_transitions_and_reframes.html] +[test_transitions_and_restyles.html] +[test_transitions_and_zoom.html] +[test_transitions_cancel_near_end.html] +[test_transitions_computed_values.html] +[test_transitions_computed_value_combinations.html] +[test_transitions_events.html] +[test_transitions.html] +skip-if = (android_version == '18' && debug) # bug 1159532 +[test_transitions_bug537151.html] +[test_transitions_dynamic_changes.html] +[test_transitions_per_property.html] +skip-if = toolkit == 'android' #bug 775227 +[test_transitions_replacement_on_busy_frame.html] +support-files = file_transitions_replacement_on_busy_frame.html +[test_transitions_step_functions.html] +[test_transitions_with_disabled_properties.html] +support-files = file_transitions_with_disabled_properties.html +[test_unclosed_parentheses.html] +[test_unicode_range_loading.html] +support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff +[test_units_angle.html] +[test_units_frequency.html] +[test_units_length.html] +[test_units_time.html] +[test_unprefixing_service.html] +support-files = unprefixing_service_iframe.html unprefixing_service_utils.js +[test_unprefixing_service_prefs.html] +support-files = unprefixing_service_iframe.html unprefixing_service_utils.js +[test_value_cloning.html] +skip-if = toolkit == 'android' #bug 775227 +[test_value_computation.html] +skip-if = toolkit == 'android' +[test_value_storage.html] +[test_variable_serialization_computed.html] +[test_variable_serialization_specified.html] +[test_variables.html] +support-files = support/external-variable-url.css +[test_video_object_fit.html] +[test_viewport_units.html] +[test_visited_image_loading.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_visited_image_loading_empty.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_visited_lying.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_visited_pref.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_visited_reftests.html] +skip-if = toolkit == 'android' #TIMED_OUT +[test_webkit_device_pixel_ratio.html] +[test_webkit_flex_display.html] +[test_asyncopen2.html] +[test_align_shorthand_serialization.html] diff --git a/layout/style/test/moz.build b/layout/style/test/moz.build new file mode 100644 index 000000000..ca61f607a --- /dev/null +++ b/layout/style/test/moz.build @@ -0,0 +1,126 @@ +# -*- 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/. + +# ** Note: The comment below along with the CPP_UNIT_TESTS and LIBS variables +# ** were commented out in the original Makefile.in, and should be restored +# ** some day, perhaps as a gtest. +# +# ParseCSS.cpp used to be built as a test program, but it was not +# being used for anything, and recent changes to the CSS loader have +# made it fail to link. Further changes are planned which should make +# it buildable again. +# +# TestCSSPropertyLookup.cpp needs the internal XPCOM APIs and so cannot +# be built with libxul enabled. +# +#CPP_UNIT_TESTS = TestCSSPropertyLookup.cpp +#LIBS += ../nsCSSKeywords.$(OBJ_SUFFIX) ../nsCSSProps.$(OBJ_SUFFIX) $(XPCOM_LIBS) + +HAS_MISC_RULE = True + +HostSimplePrograms([ + 'host_ListCSSProperties', +]) + +MOCHITEST_MANIFESTS += [ + 'mochitest.ini', +] +XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini'] +BROWSER_CHROME_MANIFESTS += ['browser.ini'] +MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini'] + +TEST_HARNESS_FILES.testing.mochitest.tests.layout.style.test.chrome += [ + 'chrome/moz_document_helper.html', +] + +TEST_HARNESS_FILES.testing.mochitest.tests.layout.style.test['css-visited'] += [ + '/layout/reftests/css-visited/border-1-ref.html', + '/layout/reftests/css-visited/border-1.html', + '/layout/reftests/css-visited/border-2-ref.html', + '/layout/reftests/css-visited/border-2a.html', + '/layout/reftests/css-visited/border-2b.html', + '/layout/reftests/css-visited/border-collapse-1-ref.html', + '/layout/reftests/css-visited/border-collapse-1.html', + '/layout/reftests/css-visited/color-choice-1-ref.html', + '/layout/reftests/css-visited/color-choice-1.html', + '/layout/reftests/css-visited/color-on-bullets-1-ref.html', + '/layout/reftests/css-visited/color-on-bullets-1.html', + '/layout/reftests/css-visited/color-on-link-1-ref.html', + '/layout/reftests/css-visited/color-on-link-1.html', + '/layout/reftests/css-visited/color-on-link-before-1.html', + '/layout/reftests/css-visited/color-on-text-decoration-1-ref.html', + '/layout/reftests/css-visited/color-on-text-decoration-1.html', + '/layout/reftests/css-visited/color-on-visited-1-ref.html', + '/layout/reftests/css-visited/color-on-visited-1.html', + '/layout/reftests/css-visited/color-on-visited-before-1.html', + '/layout/reftests/css-visited/column-rule-1-notref.html', + '/layout/reftests/css-visited/column-rule-1-ref.html', + '/layout/reftests/css-visited/column-rule-1.html', + '/layout/reftests/css-visited/content-before-1-ref.html', + '/layout/reftests/css-visited/content-color-on-link-before-1-ref.html', + '/layout/reftests/css-visited/content-color-on-link-before-1.html', + '/layout/reftests/css-visited/content-color-on-visited-before-1-ref.html', + '/layout/reftests/css-visited/content-color-on-visited-before-1.html', + '/layout/reftests/css-visited/content-on-link-before-1.html', + '/layout/reftests/css-visited/content-on-visited-before-1.html', + '/layout/reftests/css-visited/first-line-1-ref.html', + '/layout/reftests/css-visited/first-line-1.html', + '/layout/reftests/css-visited/inherit-keyword-1-ref.html', + '/layout/reftests/css-visited/inherit-keyword-1.xhtml', + '/layout/reftests/css-visited/link-root-1-ref.xhtml', + '/layout/reftests/css-visited/link-root-1.xhtml', + '/layout/reftests/css-visited/mathml-links-ref.html', + '/layout/reftests/css-visited/mathml-links.html', + '/layout/reftests/css-visited/outline-1-ref.html', + '/layout/reftests/css-visited/outline-1.html', + '/layout/reftests/css-visited/selector-adj-sibling-1-ref.html', + '/layout/reftests/css-visited/selector-adj-sibling-1.html', + '/layout/reftests/css-visited/selector-adj-sibling-2-ref.html', + '/layout/reftests/css-visited/selector-adj-sibling-2.html', + '/layout/reftests/css-visited/selector-any-sibling-1-ref.html', + '/layout/reftests/css-visited/selector-any-sibling-1.html', + '/layout/reftests/css-visited/selector-any-sibling-2-ref.html', + '/layout/reftests/css-visited/selector-any-sibling-2.html', + '/layout/reftests/css-visited/selector-child-1-ref.html', + '/layout/reftests/css-visited/selector-child-1.html', + '/layout/reftests/css-visited/selector-child-2-ref.xhtml', + '/layout/reftests/css-visited/selector-child-2.xhtml', + '/layout/reftests/css-visited/selector-descendant-1-ref.html', + '/layout/reftests/css-visited/selector-descendant-1.html', + '/layout/reftests/css-visited/selector-descendant-2-ref.xhtml', + '/layout/reftests/css-visited/selector-descendant-2.xhtml', + '/layout/reftests/css-visited/subject-of-selector-1-ref.html', + '/layout/reftests/css-visited/subject-of-selector-adj-sibling-1.html', + '/layout/reftests/css-visited/subject-of-selector-any-sibling-1.html', + '/layout/reftests/css-visited/subject-of-selector-child-1.html', + '/layout/reftests/css-visited/subject-of-selector-descendant-1.html', + '/layout/reftests/css-visited/subject-of-selector-descendant-2-ref.xhtml', + '/layout/reftests/css-visited/subject-of-selector-descendant-2.xhtml', + '/layout/reftests/css-visited/visited-page.html', + '/layout/reftests/css-visited/white-to-transparent-1-ref.html', + '/layout/reftests/css-visited/white-to-transparent-1.html', + '/layout/reftests/css-visited/width-1-ref.html', + '/layout/reftests/css-visited/width-on-link-1.html', + '/layout/reftests/css-visited/width-on-visited-1.html', + '/layout/reftests/svg/as-image/lime100x100.svg', + '/layout/reftests/svg/as-image/svg-image-visited-1-helper.svg', + '/layout/reftests/svg/as-image/svg-image-visited-2-helper.svg', + '/layout/reftests/svg/pseudo-classes-02-ref.svg', + '/layout/reftests/svg/pseudo-classes-02.svg', +] + +DEFINES['MOZILLA_INTERNAL_API'] = True +if CONFIG['MOZ_ENABLE_MASK_AS_SHORTHAND']: + HOST_DEFINES['MOZ_ENABLE_MASK_AS_SHORTHAND'] = True + +if CONFIG['COMPILE_ENVIRONMENT']: + GENERATED_FILES += ['css_properties.js'] + GENERATED_FILES['css_properties.js'].script = 'gen-css-properties.py' + GENERATED_FILES['css_properties.js'].inputs = [ + 'css_properties_like_longhand.js', + '!host_ListCSSProperties%s' % CONFIG['HOST_BIN_SUFFIX'], + ] + TEST_HARNESS_FILES.testing.mochitest.tests.layout.style.test += ['!css_properties.js'] diff --git a/layout/style/test/neverending_font_load.sjs b/layout/style/test/neverending_font_load.sjs new file mode 100644 index 000000000..7bf419aaf --- /dev/null +++ b/layout/style/test/neverending_font_load.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) +{ + response.processAsync(); + response.setHeader("Content-Type", "application/octet-stream", false); + response.write(""); +} diff --git a/layout/style/test/neverending_stylesheet_load.sjs b/layout/style/test/neverending_stylesheet_load.sjs new file mode 100644 index 000000000..386ffbe35 --- /dev/null +++ b/layout/style/test/neverending_stylesheet_load.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) +{ + response.processAsync(); + response.setHeader("Content-Type", "text/css", false); + response.write(""); +} diff --git a/layout/style/test/newtab_share_rule_processors.html b/layout/style/test/newtab_share_rule_processors.html new file mode 100644 index 000000000..bdfed1145 --- /dev/null +++ b/layout/style/test/newtab_share_rule_processors.html @@ -0,0 +1,22 @@ + + +

    Hello

    + diff --git a/layout/style/test/post-redirect-1.css b/layout/style/test/post-redirect-1.css new file mode 100644 index 000000000..c2ae5d6eb --- /dev/null +++ b/layout/style/test/post-redirect-1.css @@ -0,0 +1 @@ +#one { color: green; background: url("#"); } diff --git a/layout/style/test/post-redirect-2.css b/layout/style/test/post-redirect-2.css new file mode 100644 index 000000000..0a75299ce --- /dev/null +++ b/layout/style/test/post-redirect-2.css @@ -0,0 +1 @@ +#two { color: green; background: url("#"); } diff --git a/layout/style/test/post-redirect-3.css b/layout/style/test/post-redirect-3.css new file mode 100644 index 000000000..b33887ae3 --- /dev/null +++ b/layout/style/test/post-redirect-3.css @@ -0,0 +1 @@ +#three { color: green; background: url("#"); } diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js new file mode 100644 index 000000000..9c69e7d10 --- /dev/null +++ b/layout/style/test/property_database.js @@ -0,0 +1,7917 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Utility function. Returns true if the given boolean pref... +// (a) exists and (b) is set to true. +// Otherwise, returns false. +// +// This function also reports a test failure if the pref isn't set at all. This +// ensures that we remove pref-checks from mochitests (instead of accidentally +// disabling the tests that are controlled by that check) when we remove a +// mature feature's pref from the rest of the codebase. +function IsCSSPropertyPrefEnabled(prefName) +{ + try { + if (SpecialPowers.getBoolPref(prefName)) { + return true; + } + } catch (ex) { + ok(false, "Failed to look up property-controlling pref '" + + prefName + "' (" + ex + ")"); + } + + return false; +} + +// True longhand properties. +const CSS_TYPE_LONGHAND = 0; + +// True shorthand properties. +const CSS_TYPE_TRUE_SHORTHAND = 1; + +// Properties that we handle as shorthands but were longhands either in +// the current spec or earlier versions of the spec. +const CSS_TYPE_SHORTHAND_AND_LONGHAND = 2; + +// Each property has the following fields: +// domProp: The name of the relevant member of nsIDOM[NS]CSS2Properties +// inherited: Whether the property is inherited by default (stated as +// yes or no in the property header in all CSS specs) +// type: see above +// alias_for: optional, indicates that the property is an alias for +// some other property that is the preferred serialization. (Type +// must not be CSS_TYPE_LONGHAND.) +// logical: optional, indicates that the property is a logical directional +// property. (Type must be CSS_TYPE_LONGHAND.) +// axis: optional, indicates that the property is an axis-related logical +// directional property. (Type must be CSS_TYPE_LONGHAND and 'logical' +// must be true.) +// get_computed: if present, the property's computed value shows up on +// another property, and this is a function used to get it +// initial_values: Values whose computed value should be the same as the +// computed value for the property's initial value. +// other_values: Values whose computed value should be different from the +// computed value for the property's initial value. +// XXX Should have a third field for values whose computed value may or +// may not be the same as for the property's initial value. +// invalid_values: Things that are not values for the property and +// should be rejected, but which are balanced and should not absorb +// what follows +// quirks_values: Values that should be accepted in quirks mode only, +// mapped to the values they are equivalent to. +// unbalanced_values: Things that are not values for the property and +// should be rejected, and which also contain unbalanced constructs +// that should absorb what follows +// +// Note: By default, an alias is assumed to accept/reject the same values as +// the property that it aliases, and to have the same prerequisites. So, if +// "alias_for" is set, the "*_values" and "prerequisites" fields can simply +// be omitted, and they'll be populated automatically to match the aliased +// property's fields. + +// Helper functions used to construct gCSSProperties. + +function initial_font_family_is_sans_serif() +{ + // The initial value of 'font-family' might be 'serif' or + // 'sans-serif'. + var div = document.createElement("div"); + div.setAttribute("style", "font: initial"); + return getComputedStyle(div, "").fontFamily == "sans-serif"; +} +var gInitialFontFamilyIsSansSerif = initial_font_family_is_sans_serif(); + +// shared by background-image and border-image-source +var validGradientAndElementValues = [ + "-moz-element(#a)", + "-moz-element( #a )", + "-moz-element(#a-1)", + "-moz-element(#a\\:1)", + /* gradient torture test */ + "linear-gradient(red, blue)", + "linear-gradient(red, yellow, blue)", + "linear-gradient(red 1px, yellow 20%, blue 24em, green)", + "linear-gradient(red, yellow, green, blue 50%)", + "linear-gradient(red -50%, yellow -25%, green, blue)", + "linear-gradient(red -99px, yellow, green, blue 120%)", + "linear-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + "linear-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + "linear-gradient(red, green calc(50% + 20px), blue)", + + "linear-gradient(to top, red, blue)", + "linear-gradient(to bottom, red, blue)", + "linear-gradient(to left, red, blue)", + "linear-gradient(to right, red, blue)", + "linear-gradient(to top left, red, blue)", + "linear-gradient(to top right, red, blue)", + "linear-gradient(to bottom left, red, blue)", + "linear-gradient(to bottom right, red, blue)", + "linear-gradient(to left top, red, blue)", + "linear-gradient(to left bottom, red, blue)", + "linear-gradient(to right top, red, blue)", + "linear-gradient(to right bottom, red, blue)", + + "linear-gradient(-33deg, red, blue)", + "linear-gradient(30grad, red, blue)", + "linear-gradient(10deg, red, blue)", + "linear-gradient(1turn, red, blue)", + "linear-gradient(.414rad, red, blue)", + + "linear-gradient(.414rad, red, 50%, blue)", + "linear-gradient(.414rad, red, 0%, blue)", + "linear-gradient(.414rad, red, 100%, blue)", + + "linear-gradient(.414rad, red 50%, 50%, blue 50%)", + "linear-gradient(.414rad, red 50%, 20%, blue 50%)", + "linear-gradient(.414rad, red 50%, 30%, blue 10%)", + "linear-gradient(to right bottom, red, 20%, green 50%, 65%, blue)", + "linear-gradient(to right bottom, red, 20%, green 10%, blue)", + "linear-gradient(to right bottom, red, 50%, green 50%, 50%, blue)", + "linear-gradient(to right bottom, red, 0%, green 50%, 100%, blue)", + + "-moz-linear-gradient(red, blue)", + "-moz-linear-gradient(red, yellow, blue)", + "-moz-linear-gradient(red 1px, yellow 20%, blue 24em, green)", + "-moz-linear-gradient(red, yellow, green, blue 50%)", + "-moz-linear-gradient(red -50%, yellow -25%, green, blue)", + "-moz-linear-gradient(red -99px, yellow, green, blue 120%)", + "-moz-linear-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + "-moz-linear-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + + "-moz-linear-gradient(to top, red, blue)", + "-moz-linear-gradient(to bottom, red, blue)", + "-moz-linear-gradient(to left, red, blue)", + "-moz-linear-gradient(to right, red, blue)", + "-moz-linear-gradient(to top left, red, blue)", + "-moz-linear-gradient(to top right, red, blue)", + "-moz-linear-gradient(to bottom left, red, blue)", + "-moz-linear-gradient(to bottom right, red, blue)", + "-moz-linear-gradient(to left top, red, blue)", + "-moz-linear-gradient(to left bottom, red, blue)", + "-moz-linear-gradient(to right top, red, blue)", + "-moz-linear-gradient(to right bottom, red, blue)", + + "-moz-linear-gradient(top left, red, blue)", + "-moz-linear-gradient(0 0, red, blue)", + "-moz-linear-gradient(20% bottom, red, blue)", + "-moz-linear-gradient(center 20%, red, blue)", + "-moz-linear-gradient(left 35px, red, blue)", + "-moz-linear-gradient(10% 10em, red, blue)", + "-moz-linear-gradient(44px top, red, blue)", + + "-moz-linear-gradient(0px, red, blue)", + "-moz-linear-gradient(0, red, blue)", + "-moz-linear-gradient(top left 45deg, red, blue)", + "-moz-linear-gradient(20% bottom -300deg, red, blue)", + "-moz-linear-gradient(center 20% 1.95929rad, red, blue)", + "-moz-linear-gradient(left 35px 30grad, red, blue)", + "-moz-linear-gradient(left 35px 0.1turn, red, blue)", + "-moz-linear-gradient(10% 10em 99999deg, red, blue)", + "-moz-linear-gradient(44px top -33deg, red, blue)", + + "-moz-linear-gradient(-33deg, red, blue)", + "-moz-linear-gradient(30grad left 35px, red, blue)", + "-moz-linear-gradient(10deg 20px, red, blue)", + "-moz-linear-gradient(1turn 20px, red, blue)", + "-moz-linear-gradient(.414rad bottom, red, blue)", + + "-moz-linear-gradient(blue calc(0px) ,green calc(25%) ,red calc(40px) ,blue calc(60px) , yellow calc(100px))", + "-moz-linear-gradient(-33deg, blue calc(-25%) ,red 40px)", + "-moz-linear-gradient(10deg, blue calc(100px + -25%),red calc(40px))", + "-moz-linear-gradient(10deg, blue calc(-25px),red calc(100%))", + "-moz-linear-gradient(.414rad, blue calc(100px + -25px) ,green calc(100px + -25px) ,red calc(100px + -25%) ,blue calc(-25px) , yellow calc(-25px))", + "-moz-linear-gradient(1turn, blue calc(-25%) ,green calc(25px) ,red calc(25%),blue calc(0px),white 50px, yellow calc(-25px))", + + "radial-gradient(red, blue)", + "radial-gradient(red, yellow, blue)", + "radial-gradient(red 1px, yellow 20%, blue 24em, green)", + "radial-gradient(red, yellow, green, blue 50%)", + "radial-gradient(red -50%, yellow -25%, green, blue)", + "radial-gradient(red -99px, yellow, green, blue 120%)", + "radial-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + + "radial-gradient(0 0, red, blue)", + "radial-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + + "radial-gradient(at top left, red, blue)", + "radial-gradient(at 20% bottom, red, blue)", + "radial-gradient(at center 20%, red, blue)", + "radial-gradient(at left 35px, red, blue)", + "radial-gradient(at 10% 10em, red, blue)", + "radial-gradient(at 44px top, red, blue)", + "radial-gradient(at 0 0, red, blue)", + + "radial-gradient(farthest-corner, red, blue)", + "radial-gradient(circle, red, blue)", + "radial-gradient(ellipse closest-corner, red, blue)", + "radial-gradient(closest-corner ellipse, red, blue)", + + "radial-gradient(43px, red, blue)", + "radial-gradient(43px 43px, red, blue)", + "radial-gradient(50% 50%, red, blue)", + "radial-gradient(43px 50%, red, blue)", + "radial-gradient(50% 43px, red, blue)", + "radial-gradient(circle 43px, red, blue)", + "radial-gradient(43px circle, red, blue)", + "radial-gradient(ellipse 43px 43px, red, blue)", + "radial-gradient(ellipse 50% 50%, red, blue)", + "radial-gradient(ellipse 43px 50%, red, blue)", + "radial-gradient(ellipse 50% 43px, red, blue)", + "radial-gradient(50% 43px ellipse, red, blue)", + + "radial-gradient(farthest-corner at top left, red, blue)", + "radial-gradient(ellipse closest-corner at 45px, red, blue)", + "radial-gradient(circle farthest-side at 45px, red, blue)", + "radial-gradient(closest-side ellipse at 50%, red, blue)", + "radial-gradient(farthest-corner circle at 4em, red, blue)", + + "radial-gradient(30% 40% at top left, red, blue)", + "radial-gradient(50px 60px at 15% 20%, red, blue)", + "radial-gradient(7em 8em at 45px, red, blue)", + + "-moz-radial-gradient(red, blue)", + "-moz-radial-gradient(red, yellow, blue)", + "-moz-radial-gradient(red 1px, yellow 20%, blue 24em, green)", + "-moz-radial-gradient(red, yellow, green, blue 50%)", + "-moz-radial-gradient(red -50%, yellow -25%, green, blue)", + "-moz-radial-gradient(red -99px, yellow, green, blue 120%)", + "-moz-radial-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + + "-moz-radial-gradient(top left, red, blue)", + "-moz-radial-gradient(20% bottom, red, blue)", + "-moz-radial-gradient(center 20%, red, blue)", + "-moz-radial-gradient(left 35px, red, blue)", + "-moz-radial-gradient(10% 10em, red, blue)", + "-moz-radial-gradient(44px top, red, blue)", + + "-moz-radial-gradient(top left 45deg, red, blue)", + "-moz-radial-gradient(0 0, red, blue)", + "-moz-radial-gradient(20% bottom -300deg, red, blue)", + "-moz-radial-gradient(center 20% 1.95929rad, red, blue)", + "-moz-radial-gradient(left 35px 30grad, red, blue)", + "-moz-radial-gradient(10% 10em 99999deg, red, blue)", + "-moz-radial-gradient(44px top -33deg, red, blue)", + "-moz-radial-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + + "-moz-radial-gradient(-33deg, red, blue)", + "-moz-radial-gradient(30grad left 35px, red, blue)", + "-moz-radial-gradient(10deg 20px, red, blue)", + "-moz-radial-gradient(.414rad bottom, red, blue)", + + "-moz-radial-gradient(cover, red, blue)", + "-moz-radial-gradient(cover circle, red, blue)", + "-moz-radial-gradient(contain, red, blue)", + "-moz-radial-gradient(contain ellipse, red, blue)", + "-moz-radial-gradient(circle, red, blue)", + "-moz-radial-gradient(ellipse closest-corner, red, blue)", + "-moz-radial-gradient(farthest-side circle, red, blue)", + + "-moz-radial-gradient(top left, cover, red, blue)", + "-moz-radial-gradient(15% 20%, circle, red, blue)", + "-moz-radial-gradient(45px, ellipse closest-corner, red, blue)", + "-moz-radial-gradient(45px, farthest-side circle, red, blue)", + + "-moz-radial-gradient(99deg, cover, red, blue)", + "-moz-radial-gradient(-1.2345rad, circle, red, blue)", + "-moz-radial-gradient(399grad, ellipse closest-corner, red, blue)", + "-moz-radial-gradient(399grad, farthest-side circle, red, blue)", + + "-moz-radial-gradient(top left 99deg, cover, red, blue)", + "-moz-radial-gradient(15% 20% -1.2345rad, circle, red, blue)", + "-moz-radial-gradient(45px 399grad, ellipse closest-corner, red, blue)", + "-moz-radial-gradient(45px 399grad, farthest-side circle, red, blue)", + + "-moz-repeating-linear-gradient(red, blue)", + "-moz-repeating-linear-gradient(red, yellow, blue)", + "-moz-repeating-linear-gradient(red 1px, yellow 20%, blue 24em, green)", + "-moz-repeating-linear-gradient(red, yellow, green, blue 50%)", + "-moz-repeating-linear-gradient(red -50%, yellow -25%, green, blue)", + "-moz-repeating-linear-gradient(red -99px, yellow, green, blue 120%)", + "-moz-repeating-linear-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + "-moz-repeating-linear-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + + "-moz-repeating-linear-gradient(to top, red, blue)", + "-moz-repeating-linear-gradient(to bottom, red, blue)", + "-moz-repeating-linear-gradient(to left, red, blue)", + "-moz-repeating-linear-gradient(to right, red, blue)", + "-moz-repeating-linear-gradient(to top left, red, blue)", + "-moz-repeating-linear-gradient(to top right, red, blue)", + "-moz-repeating-linear-gradient(to bottom left, red, blue)", + "-moz-repeating-linear-gradient(to bottom right, red, blue)", + "-moz-repeating-linear-gradient(to left top, red, blue)", + "-moz-repeating-linear-gradient(to left bottom, red, blue)", + "-moz-repeating-linear-gradient(to right top, red, blue)", + "-moz-repeating-linear-gradient(to right bottom, red, blue)", + + "-moz-repeating-linear-gradient(top left, red, blue)", + "-moz-repeating-linear-gradient(0 0, red, blue)", + "-moz-repeating-linear-gradient(20% bottom, red, blue)", + "-moz-repeating-linear-gradient(center 20%, red, blue)", + "-moz-repeating-linear-gradient(left 35px, red, blue)", + "-moz-repeating-linear-gradient(10% 10em, red, blue)", + "-moz-repeating-linear-gradient(44px top, red, blue)", + + "-moz-repeating-linear-gradient(top left 45deg, red, blue)", + "-moz-repeating-linear-gradient(20% bottom -300deg, red, blue)", + "-moz-repeating-linear-gradient(center 20% 1.95929rad, red, blue)", + "-moz-repeating-linear-gradient(left 35px 30grad, red, blue)", + "-moz-repeating-linear-gradient(10% 10em 99999deg, red, blue)", + "-moz-repeating-linear-gradient(44px top -33deg, red, blue)", + + "-moz-repeating-linear-gradient(-33deg, red, blue)", + "-moz-repeating-linear-gradient(30grad left 35px, red, blue)", + "-moz-repeating-linear-gradient(10deg 20px, red, blue)", + "-moz-repeating-linear-gradient(.414rad bottom, red, blue)", + + "-moz-repeating-radial-gradient(red, blue)", + "-moz-repeating-radial-gradient(red, yellow, blue)", + "-moz-repeating-radial-gradient(red 1px, yellow 20%, blue 24em, green)", + "-moz-repeating-radial-gradient(red, yellow, green, blue 50%)", + "-moz-repeating-radial-gradient(red -50%, yellow -25%, green, blue)", + "-moz-repeating-radial-gradient(red -99px, yellow, green, blue 120%)", + "-moz-repeating-radial-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", + "-moz-repeating-radial-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + + "repeating-radial-gradient(at top left, red, blue)", + "repeating-radial-gradient(at 0 0, red, blue)", + "repeating-radial-gradient(at 20% bottom, red, blue)", + "repeating-radial-gradient(at center 20%, red, blue)", + "repeating-radial-gradient(at left 35px, red, blue)", + "repeating-radial-gradient(at 10% 10em, red, blue)", + "repeating-radial-gradient(at 44px top, red, blue)", + + "-moz-repeating-radial-gradient(farthest-corner, red, blue)", + "-moz-repeating-radial-gradient(circle, red, blue)", + "-moz-repeating-radial-gradient(ellipse closest-corner, red, blue)", + + "repeating-radial-gradient(farthest-corner at top left, red, blue)", + "repeating-radial-gradient(closest-corner ellipse at 45px, red, blue)", + "repeating-radial-gradient(farthest-side circle at 45px, red, blue)", + "repeating-radial-gradient(ellipse closest-side at 50%, red, blue)", + "repeating-radial-gradient(circle farthest-corner at 4em, red, blue)", + + "repeating-radial-gradient(30% 40% at top left, red, blue)", + "repeating-radial-gradient(50px 60px at 15% 20%, red, blue)", + "repeating-radial-gradient(7em 8em at 45px, red, blue)", + + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 2, 10, 10, 2)", + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 10%, 50%, 30%, 0%)", + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), 10, 50%, 30%, 0)", + + "-moz-radial-gradient(calc(25%) top, red, blue)", + "-moz-radial-gradient(left calc(25%), red, blue)", + "-moz-radial-gradient(calc(25px) top, red, blue)", + "-moz-radial-gradient(left calc(25px), red, blue)", + "-moz-radial-gradient(calc(-25%) top, red, blue)", + "-moz-radial-gradient(left calc(-25%), red, blue)", + "-moz-radial-gradient(calc(-25px) top, red, blue)", + "-moz-radial-gradient(left calc(-25px), red, blue)", + "-moz-radial-gradient(calc(100px + -25%) top, red, blue)", + "-moz-radial-gradient(left calc(100px + -25%), red, blue)", + "-moz-radial-gradient(calc(100px + -25px) top, red, blue)", + "-moz-radial-gradient(left calc(100px + -25px), red, blue)" +]; +var invalidGradientAndElementValues = [ + "-moz-element(#a:1)", + "-moz-element(a#a)", + "-moz-element(#a a)", + "-moz-element(#a+a)", + "-moz-element(#a())", + /* no quirks mode colors */ + "linear-gradient(red, ff00ff)", + /* no quirks mode colors */ + "-moz-radial-gradient(10% bottom, ffffff, black) scroll no-repeat", + /* no quirks mode lengths */ + "-moz-linear-gradient(10 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat", + "linear-gradient(red -99, yellow, green, blue 120%)", + /* Unitless 0 is invalid as an */ + "-moz-linear-gradient(top left 0, red, blue)", + "-moz-linear-gradient(5px 5px 0, red, blue)", + "linear-gradient(0, red, blue)", + /* Invalid color, calc() or -moz-image-rect() function */ + "linear-gradient(red, rgb(0, rubbish, 0) 50%, red)", + "linear-gradient(red, red calc(50% + rubbish), red)", + "linear-gradient(to top calc(50% + rubbish), red, blue)", + /* Old syntax */ + "-moz-linear-gradient(10px 10px, 20px, 30px 30px, 40px, from(blue), to(red))", + "-moz-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))", + "-moz-radial-gradient(10px 10px, 20%, 40px 40px, 10px, from(green), to(#ff00ff))", + "-moz-linear-gradient(10px, 20px, 30px, 40px, color-stop(0.5, #00ccff))", + "-moz-linear-gradient(20px 20px, from(blue), to(red))", + "-moz-linear-gradient(40px 40px, 10px 10px, from(blue) to(red) color-stop(10%, fuchsia))", + "-moz-linear-gradient(20px 20px 30px, 10px 10px, from(red), to(#ff0000))", + "-moz-radial-gradient(left top, center, 20px 20px, 10px, from(blue), to(red))", + "-moz-linear-gradient(left left, top top, from(blue))", + "-moz-linear-gradient(inherit, 10px 10px, from(blue))", + /* New syntax */ + "-moz-linear-gradient(10px 10px, 20px, 30px 30px, 40px, blue 0, red 100%)", + "-moz-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))", + "-moz-radial-gradient(10px 10px, 20%, 40px 40px, 10px, from(green), to(#ff00ff))", + "-moz-linear-gradient(10px, 20px, 30px, 40px, #00ccff 50%)", + "-moz-linear-gradient(40px 40px, 10px 10px, blue 0 fuchsia 10% red 100%)", + "-moz-linear-gradient(20px 20px 30px, 10px 10px, red 0, #ff0000 100%)", + "-moz-radial-gradient(left top, center, 20px 20px, 10px, from(blue), to(red))", + "-moz-linear-gradient(left left, top top, blue 0)", + "-moz-linear-gradient(inherit, 10px 10px, blue 0)", + "-moz-linear-gradient(left left blue red)", + "-moz-linear-gradient(left left blue, red)", + "-moz-linear-gradient()", + "-moz-linear-gradient(cover, red, blue)", + "-moz-linear-gradient(auto, red, blue)", + "-moz-linear-gradient(22 top, red, blue)", + "-moz-linear-gradient(10% red blue)", + "-moz-linear-gradient(10%, red blue)", + "-moz-linear-gradient(10%,, red, blue)", + "-moz-linear-gradient(45px, center, red, blue)", + "-moz-linear-gradient(45px, center red, blue)", + "-moz-radial-gradient(contain, ellipse, red, blue)", + "-moz-radial-gradient(10deg contain, red, blue)", + "-moz-radial-gradient(10deg, contain,, red, blue)", + "-moz-radial-gradient(contain contain, red, blue)", + "-moz-radial-gradient(ellipse circle, red, blue)", + "-moz-radial-gradient(to top left, red, blue)", + "-moz-radial-gradient(center, 10%, red, blue)", + "-moz-radial-gradient(5rad, 20px, red, blue)", + "-moz-radial-gradient(40%, -100px -10%, red, blue)", + + "-moz-radial-gradient(at top left to cover, red, blue)", + "-moz-radial-gradient(at 15% 20% circle, red, blue)", + + "-moz-radial-gradient(to cover, red, blue)", + "-moz-radial-gradient(to contain, red, blue)", + "-moz-radial-gradient(to closest-side circle, red, blue)", + "-moz-radial-gradient(to farthest-corner ellipse, red, blue)", + + "-moz-radial-gradient(ellipse at 45px closest-corner, red, blue)", + "-moz-radial-gradient(circle at 45px farthest-side, red, blue)", + "-moz-radial-gradient(ellipse 45px, closest-side, red, blue)", + "-moz-radial-gradient(circle 45px, farthest-corner, red, blue)", + "-moz-radial-gradient(ellipse, ellipse closest-side, red, blue)", + "-moz-radial-gradient(circle, circle farthest-corner, red, blue)", + + "-moz-radial-gradient(99deg to farthest-corner, red, blue)", + "-moz-radial-gradient(-1.2345rad circle, red, blue)", + "-moz-radial-gradient(ellipse 399grad to closest-corner, red, blue)", + "-moz-radial-gradient(circle 399grad to farthest-side, red, blue)", + + "-moz-radial-gradient(at top left 99deg, to farthest-corner, red, blue)", + "-moz-radial-gradient(circle at 15% 20% -1.2345rad, red, blue)", + "-moz-radial-gradient(to top left at 30% 40%, red, blue)", + "-moz-radial-gradient(ellipse at 45px 399grad, to closest-corner, red, blue)", + "-moz-radial-gradient(at 45px 399grad to farthest-side circle, red, blue)", + + "-moz-radial-gradient(to 50%, red, blue)", + "-moz-radial-gradient(circle to 50%, red, blue)", + "-moz-radial-gradient(circle to 43px 43px, red, blue)", + "-moz-radial-gradient(circle to 50% 50%, red, blue)", + "-moz-radial-gradient(circle to 43px 50%, red, blue)", + "-moz-radial-gradient(circle to 50% 43px, red, blue)", + "-moz-radial-gradient(ellipse to 43px, red, blue)", + "-moz-radial-gradient(ellipse to 50%, red, blue)", + + "-moz-linear-gradient(to 0 0, red, blue)", + "-moz-linear-gradient(to 20% bottom, red, blue)", + "-moz-linear-gradient(to center 20%, red, blue)", + "-moz-linear-gradient(to left 35px, red, blue)", + "-moz-linear-gradient(to 10% 10em, red, blue)", + "-moz-linear-gradient(to 44px top, red, blue)", + "-moz-linear-gradient(to top left 45deg, red, blue)", + "-moz-linear-gradient(to 20% bottom -300deg, red, blue)", + "-moz-linear-gradient(to center 20% 1.95929rad, red, blue)", + "-moz-linear-gradient(to left 35px 30grad, red, blue)", + "-moz-linear-gradient(to 10% 10em 99999deg, red, blue)", + "-moz-linear-gradient(to 44px top -33deg, red, blue)", + "-moz-linear-gradient(to -33deg, red, blue)", + "-moz-linear-gradient(to 30grad left 35px, red, blue)", + "-moz-linear-gradient(to 10deg 20px, red, blue)", + "-moz-linear-gradient(to .414rad bottom, red, blue)", + + "-moz-linear-gradient(to top top, red, blue)", + "-moz-linear-gradient(to bottom bottom, red, blue)", + "-moz-linear-gradient(to left left, red, blue)", + "-moz-linear-gradient(to right right, red, blue)", + + "-moz-repeating-linear-gradient(10px 10px, 20px, 30px 30px, 40px, blue 0, red 100%)", + "-moz-repeating-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))", + "-moz-repeating-radial-gradient(10px 10px, 20%, 40px 40px, 10px, from(green), to(#ff00ff))", + "-moz-repeating-linear-gradient(10px, 20px, 30px, 40px, #00ccff 50%)", + "-moz-repeating-linear-gradient(40px 40px, 10px 10px, blue 0 fuchsia 10% red 100%)", + "-moz-repeating-linear-gradient(20px 20px 30px, 10px 10px, red 0, #ff0000 100%)", + "-moz-repeating-radial-gradient(left top, center, 20px 20px, 10px, from(blue), to(red))", + "-moz-repeating-linear-gradient(left left, top top, blue 0)", + "-moz-repeating-linear-gradient(inherit, 10px 10px, blue 0)", + "-moz-repeating-linear-gradient(left left blue red)", + "-moz-repeating-linear-gradient()", + + "-moz-repeating-linear-gradient(to 0 0, red, blue)", + "-moz-repeating-linear-gradient(to 20% bottom, red, blue)", + "-moz-repeating-linear-gradient(to center 20%, red, blue)", + "-moz-repeating-linear-gradient(to left 35px, red, blue)", + "-moz-repeating-linear-gradient(to 10% 10em, red, blue)", + "-moz-repeating-linear-gradient(to 44px top, red, blue)", + "-moz-repeating-linear-gradient(to top left 45deg, red, blue)", + "-moz-repeating-linear-gradient(to 20% bottom -300deg, red, blue)", + "-moz-repeating-linear-gradient(to center 20% 1.95929rad, red, blue)", + "-moz-repeating-linear-gradient(to left 35px 30grad, red, blue)", + "-moz-repeating-linear-gradient(to 10% 10em 99999deg, red, blue)", + "-moz-repeating-linear-gradient(to 44px top -33deg, red, blue)", + "-moz-repeating-linear-gradient(to -33deg, red, blue)", + "-moz-repeating-linear-gradient(to 30grad left 35px, red, blue)", + "-moz-repeating-linear-gradient(to 10deg 20px, red, blue)", + "-moz-repeating-linear-gradient(to .414rad bottom, red, blue)", + + "-moz-repeating-linear-gradient(to top top, red, blue)", + "-moz-repeating-linear-gradient(to bottom bottom, red, blue)", + "-moz-repeating-linear-gradient(to left left, red, blue)", + "-moz-repeating-linear-gradient(to right right, red, blue)", + + "-moz-repeating-radial-gradient(to top left at 30% 40%, red, blue)", + "-moz-repeating-radial-gradient(ellipse at 45px closest-corner, red, blue)", + "-moz-repeating-radial-gradient(circle at 45px farthest-side, red, blue)", + + "radial-gradient(circle 175px 20px, black, white)", + "radial-gradient(175px 20px circle, black, white)", + "radial-gradient(ellipse 175px, black, white)", + "radial-gradient(175px ellipse, black, white)", + "radial-gradient(50%, red, blue)", + "radial-gradient(circle 50%, red, blue)", + "radial-gradient(50% circle, red, blue)", + + /* Valid only when prefixed */ + "linear-gradient(top left, red, blue)", + "linear-gradient(0 0, red, blue)", + "linear-gradient(20% bottom, red, blue)", + "linear-gradient(center 20%, red, blue)", + "linear-gradient(left 35px, red, blue)", + "linear-gradient(10% 10em, red, blue)", + "linear-gradient(44px top, red, blue)", + + "linear-gradient(top left 45deg, red, blue)", + "linear-gradient(20% bottom -300deg, red, blue)", + "linear-gradient(center 20% 1.95929rad, red, blue)", + "linear-gradient(left 35px 30grad, red, blue)", + "linear-gradient(left 35px 0.1turn, red, blue)", + "linear-gradient(10% 10em 99999deg, red, blue)", + "linear-gradient(44px top -33deg, red, blue)", + + "linear-gradient(30grad left 35px, red, blue)", + "linear-gradient(10deg 20px, red, blue)", + "linear-gradient(1turn 20px, red, blue)", + "linear-gradient(.414rad bottom, red, blue)", + + "linear-gradient(to top, 0%, blue)", + "linear-gradient(to top, red, 100%)", + "linear-gradient(to top, red, 45%, 56%, blue)", + "linear-gradient(to top, red,, blue)", + "linear-gradient(to top, red, green 35%, 15%, 54%, blue)", + + + "radial-gradient(top left 45deg, red, blue)", + "radial-gradient(20% bottom -300deg, red, blue)", + "radial-gradient(center 20% 1.95929rad, red, blue)", + "radial-gradient(left 35px 30grad, red, blue)", + "radial-gradient(10% 10em 99999deg, red, blue)", + "radial-gradient(44px top -33deg, red, blue)", + + "radial-gradient(-33deg, red, blue)", + "radial-gradient(30grad left 35px, red, blue)", + "radial-gradient(10deg 20px, red, blue)", + "radial-gradient(.414rad bottom, red, blue)", + + "radial-gradient(cover, red, blue)", + "radial-gradient(ellipse contain, red, blue)", + "radial-gradient(cover circle, red, blue)", + + "radial-gradient(top left, cover, red, blue)", + "radial-gradient(15% 20%, circle, red, blue)", + "radial-gradient(45px, ellipse closest-corner, red, blue)", + "radial-gradient(45px, farthest-side circle, red, blue)", + + "radial-gradient(99deg, cover, red, blue)", + "radial-gradient(-1.2345rad, circle, red, blue)", + "radial-gradient(399grad, ellipse closest-corner, red, blue)", + "radial-gradient(399grad, farthest-side circle, red, blue)", + + "radial-gradient(top left 99deg, cover, red, blue)", + "radial-gradient(15% 20% -1.2345rad, circle, red, blue)", + "radial-gradient(45px 399grad, ellipse closest-corner, red, blue)", + "radial-gradient(45px 399grad, farthest-side circle, red, blue)", + + /* Valid only when unprefixed */ + "-moz-radial-gradient(at top left, red, blue)", + "-moz-radial-gradient(at 20% bottom, red, blue)", + "-moz-radial-gradient(at center 20%, red, blue)", + "-moz-radial-gradient(at left 35px, red, blue)", + "-moz-radial-gradient(at 10% 10em, red, blue)", + "-moz-radial-gradient(at 44px top, red, blue)", + "-moz-radial-gradient(at 0 0, red, blue)", + + "-moz-radial-gradient(circle 43px, red, blue)", + "-moz-radial-gradient(ellipse 43px 43px, red, blue)", + "-moz-radial-gradient(ellipse 50% 50%, red, blue)", + "-moz-radial-gradient(ellipse 43px 50%, red, blue)", + "-moz-radial-gradient(ellipse 50% 43px, red, blue)", + + "-moz-radial-gradient(farthest-corner at top left, red, blue)", + "-moz-radial-gradient(ellipse closest-corner at 45px, red, blue)", + "-moz-radial-gradient(circle farthest-side at 45px, red, blue)", + "-moz-radial-gradient(closest-side ellipse at 50%, red, blue)", + "-moz-radial-gradient(farthest-corner circle at 4em, red, blue)", + + "-moz-radial-gradient(30% 40% at top left, red, blue)", + "-moz-radial-gradient(50px 60px at 15% 20%, red, blue)", + "-moz-radial-gradient(7em 8em at 45px, red, blue)" +]; +var unbalancedGradientAndElementValues = [ + "-moz-element(#a()", +]; + +if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { + // Extend gradient lists with valid/invalid webkit-prefixed expressions: + validGradientAndElementValues.push( + // 2008 GRADIENTS: -webkit-gradient() + // ---------------------------------- + // linear w/ no color stops (valid) and a variety of position values: + "-webkit-gradient(linear, 1 2, 3 4)", + "-webkit-gradient(linear,1 2,3 4)", // (no extra space) + "-webkit-gradient(linear , 1 2 , 3 4 )", // (lots of extra space) + "-webkit-gradient(linear, 1 10% , 0% 4)", // percentages + "-webkit-gradient(linear, +1.0 -2%, +5.3% -0)", // (+/- & decimals are valid) + "-webkit-gradient(linear, left top, right bottom)", // keywords + "-webkit-gradient(linear, right center, center top)", + "-webkit-gradient(linear, center center, center center)", + "-webkit-gradient(linear, center 5%, 30 top)", // keywords mixed w/ nums + + // linear w/ just 1 color stop: + "-webkit-gradient(linear, 1 2, 3 4, from(lime))", + "-webkit-gradient(linear, 1 2, 3 4, to(lime))", + // * testing the various allowable stop values ( & ): + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(-0, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(-30, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(+9999, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(-.1, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0%, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(100%, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(9999%, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(-.5%, lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(+0%, lime))", + // * testing the various color values: + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, transparent))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, rgb(1,2,3)))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, #00ff00))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, #00f))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, hsla(240, 30%, 50%, 0.8)))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, rgba(255, 230, 10, 0.5)))", + + // linear w/ multiple color stops: + // * using from()/to() -- note that out-of-order is OK: + "-webkit-gradient(linear, 1 2, 3 4, from(lime), from(blue))", + "-webkit-gradient(linear, 1 2, 3 4, to(lime), to(blue))", + "-webkit-gradient(linear, 1 2, 3 4, from(lime), to(blue))", + "-webkit-gradient(linear, 1 2, 3 4, to(lime), from(blue))", + "-webkit-gradient(linear, 1 2, 3 4, from(lime), to(blue), from(purple))", + // * using color-stop(): + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime), color-stop(30%, blue))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0, lime), color-stop(30%, blue), color-stop(100%, purple))", + // * using color-stop() intermixed with from()/to() functions: + "-webkit-gradient(linear, 1 2, 3 4, from(lime), color-stop(30%, blue))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(30%, blue), to(lime))", + // * overshooting endpoints (0 & 1.0) + "-webkit-gradient(linear, 1 2, 3 4, color-stop(-30%, lime), color-stop(.4, blue), color-stop(1.5, purple))", + // * repeating a stop position (valid) + "-webkit-gradient(linear, 1 2, 3 4, color-stop(30%, lime), color-stop(30%, blue))", + // * stops out of order (valid) + "-webkit-gradient(linear, 1 2, 3 4, color-stop(70%, lime), color-stop(20%, blue), color-stop(40%, purple))", + + // radial w/ no color stops (valid) and a several different radius values: + "-webkit-gradient(radial, 1 2, 8, 3 4, 9)", + "-webkit-gradient(radial, 0 0, 10, 0 0, 5)", + "-webkit-gradient(radial, 1 2, -1.5, center center, +99999.9999)", + + // radial w/ color stops + // (mostly leaning on more-robust 'linear' tests above; just testing a few + // examples w/ radial as a sanity-check): + "-webkit-gradient(radial, 1 2, 8, 3 4, 9, from(lime))", + "-webkit-gradient(radial, 1 2, 8, 3 4, 9, to(blue))", + "-webkit-gradient(radial, 1 2, 8, 3 4, 9, color-stop(0.5, #00f), color-stop(0.8, rgba(100, 200, 0, 0.5)))", + + // 2011 GRADIENTS: -webkit-linear-gradient(), -webkit-radial -gradient() + // --------------------------------------------------------------------- + // Basic linear-gradient syntax (valid when prefixed or unprefixed): + "-webkit-linear-gradient(red, green, blue)", + + // Angled linear-gradients (valid when prefixed or unprefixed): + "-webkit-linear-gradient(135deg, red, blue)", + "-webkit-linear-gradient(280deg, red 60%, blue)", + + // Linear-gradient with unitless-0 (normally invalid for + // but accepted here for better webkit emulation): + "-webkit-linear-gradient(0, red, blue)", + "-webkit-linear-gradient(0 red, blue)", + + // Basic radial-gradient syntax (valid when prefixed or unprefixed): + "-webkit-radial-gradient(circle, white, black)", + "-webkit-radial-gradient(circle, white, black)", + "-webkit-radial-gradient(ellipse closest-side, white, black)", + "-webkit-radial-gradient(circle farthest-corner, white, black)", + + // Contain/cover keywords (valid only for -moz/-webkit prefixed): + "-webkit-radial-gradient(cover, red, blue)", + "-webkit-radial-gradient(cover circle, red, blue)", + "-webkit-radial-gradient(contain, red, blue)", + "-webkit-radial-gradient(contain ellipse, red, blue)", + + // Initial side/corner/point (valid only for -moz/-webkit prefixed): + "-webkit-linear-gradient(left, red, blue)", + "-webkit-linear-gradient(right top, red, blue)", + "-webkit-linear-gradient(top right, red, blue)", + "-webkit-radial-gradient(right, red, blue)", + "-webkit-radial-gradient(left bottom, red, blue)", + "-webkit-radial-gradient(bottom left, red, blue)", + "-webkit-radial-gradient(center, red, blue)", + "-webkit-radial-gradient(center right, red, blue)", + "-webkit-radial-gradient(center center, red, blue)", + "-webkit-radial-gradient(center top, red, blue)", + "-webkit-radial-gradient(left 50%, red, blue)", + "-webkit-radial-gradient(20px top, red, blue)", + "-webkit-radial-gradient(20em 30%, red, blue)", + + // Point + keyword-sized shape (valid only for -moz/-webkit prefixed): + "-webkit-radial-gradient(center, circle closest-corner, red, blue)", + "-webkit-radial-gradient(10px 20px, cover circle, red, blue)", + "-webkit-radial-gradient(5em 50%, ellipse contain, red, blue)", + + // Repeating examples: + "-webkit-repeating-linear-gradient(red 10%, blue 30%)", + "-webkit-repeating-linear-gradient(30deg, pink 20px, orange 70px)", + "-webkit-repeating-linear-gradient(left, red, blue)", + "-webkit-repeating-linear-gradient(left, red 10%, blue 30%)", + "-webkit-repeating-radial-gradient(circle, red, blue 10%, red 20%)", + "-webkit-repeating-radial-gradient(circle farthest-corner, gray 10px, yellow 20px)", + "-webkit-repeating-radial-gradient(top left, circle, red, blue 4%, red 8%)" + ); + + invalidGradientAndElementValues.push( + // 2008 GRADIENTS: -webkit-gradient() + // https://www.webkit.org/blog/175/introducing-css-gradients/ + // ---------------------------------- + // Mostly-empty expressions (missing most required pieces): + "-webkit-gradient()", + "-webkit-gradient( )", + "-webkit-gradient(,)", + "-webkit-gradient(bogus)", + "-webkit-gradient(linear)", + "-webkit-gradient(linear,)", + "-webkit-gradient(,linear)", + "-webkit-gradient(radial)", + "-webkit-gradient(radial,)", + + // linear w/ partial/missing expression(s) + "-webkit-gradient(linear, 1)", // Incomplete + "-webkit-gradient(linear, left)", // Incomplete + "-webkit-gradient(linear, center)", // Incomplete + "-webkit-gradient(linear, top)", // Incomplete + "-webkit-gradient(linear, 5%)", // Incomplete + "-webkit-gradient(linear, 1 2)", // Missing 2nd + "-webkit-gradient(linear, 1, 3)", // 2 incomplete s + "-webkit-gradient(linear, 1, 3 4)", // Incomplete 1st + "-webkit-gradient(linear, 1 2, 3)", // Incomplete 2nd + "-webkit-gradient(linear, 1 2, 3, 4)", // Comma inside + "-webkit-gradient(linear, 1, 2, 3 4)", // Comma inside + "-webkit-gradient(linear, 1, 2, 3, 4)", // Comma inside + + // linear w/ invalid units in expression + "-webkit-gradient(linear, 1px 2, 3 4)", + "-webkit-gradient(linear, 1 2, 3 4px)", + "-webkit-gradient(linear, 1px 2px, 3px 4px)", + "-webkit-gradient(linear, calc(1) 2, 3 4)", + "-webkit-gradient(linear, 1 2em, 3 4)", + + // linear w/ (only valid for radial) + "-webkit-gradient(linear, 1 2, 8, 3 4, 9)", + + // linear w/ out-of-order position keywords in expression + // (horizontal keyword is supposed to come first, for "x" coord) + "-webkit-gradient(linear, 0 0, top right)", + "-webkit-gradient(linear, bottom center, 0 0)", + "-webkit-gradient(linear, top bottom, 0 0)", + "-webkit-gradient(linear, bottom top, 0 0)", + "-webkit-gradient(linear, bottom top, 0 0)", + + // linear w/ trailing comma (which implies missing color-stops): + "-webkit-gradient(linear, 1 2, 3 4,)", + + // linear w/ invalid color values: + "-webkit-gradient(linear, 1 2, 3 4, from(invalidcolorname))", + "-webkit-gradient(linear, 1 2, 3 4, from(inherit))", + "-webkit-gradient(linear, 1 2, 3 4, from(initial))", + "-webkit-gradient(linear, 1 2, 3 4, from(currentColor))", + "-webkit-gradient(linear, 1 2, 3 4, from(00ff00))", + "-webkit-gradient(linear, 1 2, 3 4, from(##00ff00))", + "-webkit-gradient(linear, 1 2, 3 4, from(#00fff))", // wrong num hex digits + "-webkit-gradient(linear, 1 2, 3 4, from(xyz(0,0,0)))", // bogus color func + // Mixing and is invalid. + "-webkit-gradient(linear, 1 2, 3 4, from(rgb(100, 100%, 30)))", + + // linear w/ color stops that have comma issues + "-webkit-gradient(linear, 1 2, 3 4 from(lime))", + "-webkit-gradient(linear, 1 2, 3 4, from(lime,))", + "-webkit-gradient(linear, 1 2, 3 4, from(lime),)", + "-webkit-gradient(linear, 1 2, 3 4, from(lime) to(blue))", + "-webkit-gradient(linear, 1 2, 3 4, from(lime),, to(blue))", + "-webkit-gradient(linear, 1 2, 3 4, from(rbg(0, 0, 0,)))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0 lime))", + "-webkit-gradient(linear, 1 2, 3 4, color-stop(0,, lime))", + + // radial w/ broken /radius expression(s) + "-webkit-gradient(radial, 1)", // Incomplete + "-webkit-gradient(radial, 1 2)", // Missing radius + 2nd + "-webkit-gradient(radial, 1 2, 8)", // Missing 2nd + "-webkit-gradient(radial, 1 2, 8, 3)", // Incomplete 2nd + "-webkit-gradient(radial, 1 2, 8, 3 4)", // Missing 2nd radius + "-webkit-gradient(radial, 1 2, 3 4, 9)", // Missing 1st radius + + // radial w/ incorrect units on radius (invalid; expecting ) + "-webkit-gradient(radial, 1 2, 8%, 3 4, 9)", + "-webkit-gradient(radial, 1 2, 8px, 3 4, 9)", + "-webkit-gradient(radial, 1 2, calc(8), 3 4, 9)", + "-webkit-gradient(radial, 1 2, 8em, 3 4, 9)", + "-webkit-gradient(radial, 1 2, top, 3 4, 9)", + + // radial w/ trailing comma (which implies missing color-stops): + "-webkit-gradient(linear, 1 2, 8, 3 4, 9,)", + + // radial w/ invalid color value (mostly leaning on 'linear' test above): + "-webkit-gradient(radial, 1 2, 8, 3 4, 9, from(invalidcolorname))", + + // 2011 GRADIENTS: -webkit-linear-gradient(), -webkit-radial -gradient() + // --------------------------------------------------------------------- + // Syntax that's invalid for all types of gradients: + // * empty gradient expressions: + "-webkit-linear-gradient()", + "-webkit-radial-gradient()", + "-webkit-repeating-linear-gradient()", + "-webkit-repeating-radial-gradient()", + + // Linear syntax that's invalid for both -webkit & unprefixed, but valid + // for -moz: + // * initial which includes a length: + "-webkit-linear-gradient(10px, red, blue)", + "-webkit-linear-gradient(10px top, red, blue)", + // * initial which includes a side *and* an angle: + "-webkit-linear-gradient(bottom 30deg, red, blue)", + "-webkit-linear-gradient(30deg bottom, red, blue)", + "-webkit-linear-gradient(10px top 50deg, red, blue)", + "-webkit-linear-gradient(50deg 10px top, red, blue)", + // * initial which includes explicit "center": + "-webkit-linear-gradient(center, red, blue)", + "-webkit-linear-gradient(left center, red, blue)", + "-webkit-linear-gradient(top center, red, blue)", + "-webkit-linear-gradient(center top, red, blue)", + + // Linear syntax that's invalid for -webkit, but valid for -moz & unprefixed: + // * "to" syntax: + "-webkit-linear-gradient(to top, red, blue)", + + // * followed by angle: + "-webkit-radial-gradient(circle 10deg, red, blue)", + + // Radial syntax that's invalid for both -webkit & -moz, but valid for + // unprefixed: + // * " at " syntax: + "-webkit-radial-gradient(circle at left bottom, red, blue)", + // * explicitly-sized shape: + "-webkit-radial-gradient(circle 10px, red, blue)", + "-webkit-radial-gradient(ellipse 40px 20px, red, blue)", + + // Radial syntax that's invalid for both -webkit & unprefixed, but valid + // for -moz: + // * initial angle + "-webkit-radial-gradient(30deg, red, blue)", + // * initial angle/position combo + "-webkit-radial-gradient(top 30deg, red, blue)", + "-webkit-radial-gradient(left top 30deg, red, blue)", + "-webkit-radial-gradient(10px 20px 30deg, red, blue)" + ); +} + +var gCSSProperties = { + "animation": { + domProp: "animation", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ], + initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ], + other_values: [ "none none 0s 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) normal running 1.0", "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ], + invalid_values: [ "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial", "2s all,, 1s bounce", "2s all, , 1s bounce", "bounce 1s cubic-bezier(0, rubbish) 2s", "bounce 1s steps(rubbish) 2s" ] + }, + "animation-delay": { + domProp: "animationDelay", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0s", "0ms" ], + other_values: [ "1s", "250ms", "-100ms", "-1s", "1s, 250ms, 2.3s"], + invalid_values: [ "0", "0px" ] + }, + "animation-direction": { + domProp: "animationDirection", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "alternate", "normal, alternate", "alternate, normal", "normal, normal", "normal, normal, normal", "reverse", "alternate-reverse", "normal, reverse, alternate-reverse, alternate" ], + invalid_values: [ "normal normal", "inherit, normal", "reverse-alternate" ] + }, + "animation-duration": { + domProp: "animationDuration", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0s", "0ms" ], + other_values: [ "1s", "250ms", "1s, 250ms, 2.3s"], + invalid_values: [ "0", "0px", "-1ms", "-2s" ] + }, + "animation-fill-mode": { + domProp: "animationFillMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "forwards", "backwards", "both", "none, none", "forwards, backwards", "forwards, none", "none, both" ], + invalid_values: [ "all"] + }, + "animation-iteration-count": { + domProp: "animationIterationCount", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1" ], + other_values: [ "infinite", "0", "0.5", "7.75", "-0.0", "1, 2, 3", "infinite, 2", "1, infinite" ], + // negatives forbidden per + // http://lists.w3.org/Archives/Public/www-style/2011Mar/0355.html + invalid_values: [ "none", "-1", "-0.5", "-1, infinite", "infinite, -3" ] + }, + "animation-name": { + domProp: "animationName", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "all", "ball", "mall", "color", "bounce, bubble, opacity", "foobar", "auto", "\\32bounce", "-bounce", "-\\32bounce", "\\32 0bounce", "-\\32 0bounce", "\\2bounce", "-\\2bounce" ], + invalid_values: [ "bounce, initial", "initial, bounce", "bounce, inherit", "inherit, bounce" ] + }, + "animation-play-state": { + domProp: "animationPlayState", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "running" ], + other_values: [ "paused", "running, running", "paused, running", "paused, paused", "running, paused", "paused, running, running, running, paused, running" ], + invalid_values: [ "0" ] + }, + "animation-timing-function": { + domProp: "animationTimingFunction", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "ease" ], + other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ], + invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ] + }, + "-moz-appearance": { + domProp: "MozAppearance", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "radio", "menulist" ], + invalid_values: [] + }, + "-moz-binding": { + domProp: "MozBinding", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(foo.xml)" ], + invalid_values: [] + }, + "-moz-border-bottom-colors": { + domProp: "MozBorderBottomColors", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ], + invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ] + }, + "border-inline-end": { + domProp: "borderInlineEnd", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 green none" ] + }, + "border-inline-end-color": { + domProp: "borderInlineEndColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ] + }, + "border-inline-end-style": { + domProp: "borderInlineEndStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-inline-end-width": { + domProp: "borderInlineEndWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + prerequisites: { "border-inline-end-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%", "5" ] + }, + "border-image": { + domProp: "borderImage", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ], + initial_values: [ "none" ], + other_values: [ "url('border.png') 27 27 27 27", + "url('border.png') 27", + "stretch url('border.png')", + "url('border.png') 27 fill", + "url('border.png') 27 27 27 27 repeat", + "repeat url('border.png') 27 27 27 27", + "url('border.png') repeat 27 27 27 27", + "url('border.png') fill 27 27 27 27 repeat", + "url('border.png') fill 27 27 27 27 repeat space", + "url('border.png') 27 27 27 27 / 1em", + "27 27 27 27 / 1em url('border.png') ", + "url('border.png') 27 27 27 27 / 10 10 10 / 10 10 repeat", + "repeat 27 27 27 27 / 10 10 10 / 10 10 url('border.png')", + "url('border.png') 27 27 27 27 / / 10 10 1em", + "fill 27 27 27 27 / / 10 10 1em url('border.png')", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em repeat", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em stretch round" ], + invalid_values: [ "url('border.png') 27 27 27 27 27", + "url('border.png') 27 27 27 27 / 1em 1em 1em 1em 1em", + "url('border.png') 27 27 27 27 /", + "url('border.png') fill", + "url('border.png') fill repeat", + "fill repeat", + "url('border.png') fill / 1em", + "url('border.png') / repeat", + "url('border.png') 1 /", + "url('border.png') 1 / /", + "1 / url('border.png')", + "url('border.png') / 1", + "url('border.png') / / 1"] + }, + "border-image-source": { + domProp: "borderImageSource", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "url('border.png')" + ].concat(validGradientAndElementValues), + invalid_values: [ + "url('border.png') url('border.png')", + ].concat(invalidGradientAndElementValues), + unbalanced_values: [ + ].concat(unbalancedGradientAndElementValues) + }, + "border-image-slice": { + domProp: "borderImageSlice", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "100%", "100% 100% 100% 100%" ], + other_values: [ "0%", "10", "10 100% 0 2", "0 0 0 0", "fill 10 10", "10 10 fill" ], + invalid_values: [ "-10%", "-10", "10 10 10 10 10", "10 10 10 10 -10", "10px", "-10px", "fill", "fill fill 10px", "10px fill fill" ] + }, + "border-image-width": { + domProp: "borderImageWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "1 1 1 1" ], + other_values: [ "0", "0%", "0px", "auto auto auto auto", "10 10% auto 15px", "10px 10px 10px 10px", "10", "10 10", "10 10 10" ], + invalid_values: [ "-10", "-10px", "-10%", "10 10 10 10 10", "10 10 10 10 auto", "auto auto auto auto auto", "10px calc(nonsense)", "1px red" ], + unbalanced_values: [ "10px calc(" ] + }, + "border-image-outset": { + domProp: "borderImageOutset", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0 0 0 0" ], + other_values: [ "10px", "10", "10 10", "10 10 10", "10 10 10 10", "10px 10 10 10px" ], + invalid_values: [ "-10", "-10px", "-10%", "10%", "10 10 10 10 10", "10px calc(nonsense)", "1px red" ], + unbalanced_values: [ "10px calc(" ] + }, + "border-image-repeat": { + domProp: "borderImageRepeat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "stretch", "stretch stretch" ], + other_values: [ "round", "repeat", "stretch round", "repeat round", "stretch repeat", "round round", "repeat repeat", + "space", "stretch space", "repeat space", "round space", "space space" ], + invalid_values: [ "none", "stretch stretch stretch", "0", "10", "0%", "0px" ] + }, + "-moz-border-left-colors": { + domProp: "MozBorderLeftColors", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ], + invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ] + }, + "border-radius": { + domProp: "borderRadius", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + subproperties: [ "border-bottom-left-radius", "border-bottom-right-radius", "border-top-left-radius", "border-top-right-radius" ], + initial_values: [ "0", "0px", "0px 0 0 0px", "calc(-2px)", "calc(0px) calc(0pt)", "calc(0px) calc(0pt) calc(0px) calc(0em)" ], + other_values: [ "0%", "3%", "1px", "2em", "3em 2px", "2pt 3% 4em", "2px 2px 2px 2px", // circular + "3% / 2%", "1px / 4px", "2em / 1em", "3em 2px / 2px 3em", "2pt 3% 4em / 4pt 1% 5em", "2px 2px 2px 2px / 4px 4px 4px 4px", "1pt / 2pt 3pt", "4pt 5pt / 3pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + "2px 2px calc(2px + 1%) 2px", + "1px 2px 2px 2px / 2px 2px calc(2px + 1%) 2px", + ], + invalid_values: [ "2px -2px", "inherit 2px", "inherit / 2px", "2px inherit", "2px / inherit", "2px 2px 2px 2px 2px", "1px / 2px 2px 2px 2px 2px", "2", "2 2", "2px 2px 2px 2px / 2px 2px 2 2px", "2px calc(0px + rubbish)" ] + }, + "border-bottom-left-radius": { + domProp: "borderBottomLeftRadius", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] + }, + "border-bottom-right-radius": { + domProp: "borderBottomRightRadius", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] + }, + "border-top-left-radius": { + domProp: "borderTopLeftRadius", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] + }, + "border-top-right-radius": { + domProp: "borderTopRightRadius", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] + }, + "-moz-border-right-colors": { + domProp: "MozBorderRightColors", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ], + invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ] + }, + "border-inline-start": { + domProp: "borderInlineStart", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-inline-start-color", "border-inline-start-style", "border-inline-start-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 green solid" ] + }, + "border-inline-start-color": { + domProp: "borderInlineStartColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ] + }, + "border-inline-start-style": { + domProp: "borderInlineStartStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-inline-start-width": { + domProp: "borderInlineStartWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + prerequisites: { "border-inline-start-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%", "5" ] + }, + "-moz-border-top-colors": { + domProp: "MozBorderTopColors", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "red green", "red #fc3", "#ff00cc", "currentColor", "blue currentColor orange currentColor" ], + invalid_values: [ "red none", "red inherit", "red, green", "none red", "inherit red", "ff00cc" ] + }, + "-moz-box-align": { + domProp: "MozBoxAlign", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "stretch" ], + other_values: [ "start", "center", "baseline", "end" ], + invalid_values: [] + }, + "-moz-box-direction": { + domProp: "MozBoxDirection", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "reverse" ], + invalid_values: [] + }, + "-moz-box-flex": { + domProp: "MozBoxFlex", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0.0", "-0.0" ], + other_values: [ "1", "100", "0.1" ], + invalid_values: [ "10px", "-1" ] + }, + "-moz-box-ordinal-group": { + domProp: "MozBoxOrdinalGroup", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1" ], + other_values: [ "2", "100", "0" ], + invalid_values: [ "1.0", "-1", "-1000" ] + }, + "-moz-box-orient": { + domProp: "MozBoxOrient", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "horizontal", "inline-axis" ], + other_values: [ "vertical", "block-axis" ], + invalid_values: [] + }, + "-moz-box-pack": { + domProp: "MozBoxPack", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "start" ], + other_values: [ "center", "end", "justify" ], + invalid_values: [] + }, + "box-sizing": { + domProp: "boxSizing", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "content-box" ], + other_values: [ "border-box" ], + invalid_values: [ "padding-box", "margin-box", "content", "padding", "border", "margin" ] + }, + "-moz-box-sizing": { + domProp: "MozBoxSizing", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "box-sizing", + subproperties: [ "box-sizing" ], + }, + "color-adjust": { + domProp: "colorAdjust", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "economy" ], + other_values: [ "exact" ], + invalid_values: [] + }, + "columns": { + domProp: "columns", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "column-count", "column-width" ], + initial_values: [ "auto", "auto auto" ], + other_values: [ "3", "20px", "2 10px", "10px 2", "2 auto", "auto 2", "auto 50px", "50px auto" ], + invalid_values: [ "5%", "-1px", "-1", "3 5", "10px 4px", "10 2px 5in", "30px -1", + "auto 3 5px", "5 auto 20px", "auto auto auto", "calc(50px + rubbish) 2" ] + }, + "-moz-columns": { + domProp: "MozColumns", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "columns", + subproperties: [ "column-count", "column-width" ] + }, + "column-count": { + domProp: "columnCount", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "1", "17" ], + // negative and zero invalid per editor's draft + invalid_values: [ "-1", "0", "3px" ] + }, + "-moz-column-count": { + domProp: "MozColumnCount", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-count", + subproperties: [ "column-count" ] + }, + "column-fill": { + domProp: "columnFill", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "balance" ], + other_values: [ "auto" ], + invalid_values: [ "2px", "dotted", "5em" ] + }, + "-moz-column-fill": { + domProp: "MozColumnFill", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-fill", + subproperties: [ "column-fill" ] + }, + "column-gap": { + domProp: "columnGap", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal", "1em", "calc(-2em + 3em)" ], + other_values: [ "2px", "4em", + "calc(2px)", + "calc(-2px)", + "calc(0px)", + "calc(0pt)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "3%", "-1px", "4" ] + }, + "-moz-column-gap": { + domProp: "MozColumnGap", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-gap", + subproperties: [ "column-gap" ] + }, + "column-rule": { + domProp: "columnRule", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "color": "green" }, + subproperties: [ "column-rule-width", "column-rule-style", "column-rule-color" ], + initial_values: [ "medium none currentColor", "none", "medium", "currentColor" ], + other_values: [ "2px blue solid", "red dotted 1px", "ridge 4px orange", "5px solid" ], + invalid_values: [ "2px 3px 4px red", "dotted dashed", "5px dashed green 3px", "5 solid", "5 green solid" ] + }, + "-moz-column-rule": { + domProp: "MozColumnRule", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "column-rule", + subproperties: [ "column-rule-width", "column-rule-style", "column-rule-color" ] + }, + "column-rule-width": { + domProp: "columnRuleWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "-moz-column-rule-style": "solid" }, + initial_values: [ + "medium", + "3px", + "-moz-calc(3px)", + "-moz-calc(5em + 3px - 5em)", + "calc(3px)", + "calc(5em + 3px - 5em)", + ], + other_values: [ "thin", "15px", + /* valid -moz-calc() values */ + "-moz-calc(-2px)", + "-moz-calc(2px)", + "-moz-calc(3em)", + "-moz-calc(3em + 2px)", + "-moz-calc( 3em + 2px)", + "-moz-calc(3em + 2px )", + "-moz-calc( 3em + 2px )", + "-moz-calc(3*25px)", + "-moz-calc(3 *25px)", + "-moz-calc(3 * 25px)", + "-moz-calc(3* 25px)", + "-moz-calc(25px*3)", + "-moz-calc(25px *3)", + "-moz-calc(25px* 3)", + "-moz-calc(25px * 3)", + "-moz-calc(25px * 3 / 4)", + "-moz-calc((25px * 3) / 4)", + "-moz-calc(25px * (3 / 4))", + "-moz-calc(3 * 25px / 4)", + "-moz-calc((3 * 25px) / 4)", + "-moz-calc(3 * (25px / 4))", + "-moz-calc(3em + 25px * 3 / 4)", + "-moz-calc(3em + (25px * 3) / 4)", + "-moz-calc(3em + 25px * (3 / 4))", + "-moz-calc(25px * 3 / 4 + 3em)", + "-moz-calc((25px * 3) / 4 + 3em)", + "-moz-calc(25px * (3 / 4) + 3em)", + "-moz-calc(3em + (25px * 3 / 4))", + "-moz-calc(3em + ((25px * 3) / 4))", + "-moz-calc(3em + (25px * (3 / 4)))", + "-moz-calc((25px * 3 / 4) + 3em)", + "-moz-calc(((25px * 3) / 4) + 3em)", + "-moz-calc((25px * (3 / 4)) + 3em)", + "-moz-calc(3*25px + 1in)", + "-moz-calc(1in - 3em + 2px)", + "-moz-calc(1in - (3em + 2px))", + "-moz-calc((1in - 3em) + 2px)", + "-moz-calc(50px/2)", + "-moz-calc(50px/(2 - 1))", + "-moz-calc(-3px)", + /* numeric reduction cases */ + "-moz-calc(5 * 3 * 2em)", + "-moz-calc(2em * 5 * 3)", + "-moz-calc((5 * 3) * 2em)", + "-moz-calc(2em * (5 * 3))", + "-moz-calc((5 + 3) * 2em)", + "-moz-calc(2em * (5 + 3))", + "-moz-calc(2em / (5 + 3))", + "-moz-calc(2em * (5*2 + 3))", + "-moz-calc(2em * ((5*2) + 3))", + "-moz-calc(2em * (5*(2 + 3)))", + + "-moz-calc((5 + 7) * 3em)", + "-moz-calc((5em + 3em) - 2em)", + "-moz-calc((5em - 3em) + 2em)", + "-moz-calc(2em - (5em - 3em))", + "-moz-calc(2em + (5em - 3em))", + "-moz-calc(2em - (5em + 3em))", + "-moz-calc(2em + (5em + 3em))", + "-moz-calc(2em + 5em - 3em)", + "-moz-calc(2em - 5em - 3em)", + "-moz-calc(2em + 5em + 3em)", + "-moz-calc(2em - 5em + 3em)", + + "-moz-calc(2em / 4 * 3)", + "-moz-calc(2em * 4 / 3)", + "-moz-calc(2em * 4 * 3)", + "-moz-calc(2em / 4 / 3)", + "-moz-calc(4 * 2em / 3)", + "-moz-calc(4 / 3 * 2em)", + + "-moz-calc((2em / 4) * 3)", + "-moz-calc((2em * 4) / 3)", + "-moz-calc((2em * 4) * 3)", + "-moz-calc((2em / 4) / 3)", + "-moz-calc((4 * 2em) / 3)", + "-moz-calc((4 / 3) * 2em)", + + "-moz-calc(2em / (4 * 3))", + "-moz-calc(2em * (4 / 3))", + "-moz-calc(2em * (4 * 3))", + "-moz-calc(2em / (4 / 3))", + "-moz-calc(4 * (2em / 3))", + + // Valid cases with unitless zero (which is never + // a length). + "-moz-calc(0 * 2em)", + "-moz-calc(2em * 0)", + "-moz-calc(3em + 0 * 2em)", + "-moz-calc(3em + 2em * 0)", + "-moz-calc((0 + 2) * 2em)", + "-moz-calc((2 + 0) * 2em)", + // And test zero lengths while we're here. + "-moz-calc(2 * 0px)", + "-moz-calc(0 * 0px)", + "-moz-calc(2 * 0em)", + "-moz-calc(0 * 0em)", + "-moz-calc(0px * 0)", + "-moz-calc(0px * 2)", + + /* valid calc() values */ + "calc(-2px)", + "calc(2px)", + "calc(3em)", + "calc(3em + 2px)", + "calc( 3em + 2px)", + "calc(3em + 2px )", + "calc( 3em + 2px )", + "calc(3*25px)", + "calc(3 *25px)", + "calc(3 * 25px)", + "calc(3* 25px)", + "calc(25px*3)", + "calc(25px *3)", + "calc(25px* 3)", + "calc(25px * 3)", + "calc(25px * 3 / 4)", + "calc((25px * 3) / 4)", + "calc(25px * (3 / 4))", + "calc(3 * 25px / 4)", + "calc((3 * 25px) / 4)", + "calc(3 * (25px / 4))", + "calc(3em + 25px * 3 / 4)", + "calc(3em + (25px * 3) / 4)", + "calc(3em + 25px * (3 / 4))", + "calc(25px * 3 / 4 + 3em)", + "calc((25px * 3) / 4 + 3em)", + "calc(25px * (3 / 4) + 3em)", + "calc(3em + (25px * 3 / 4))", + "calc(3em + ((25px * 3) / 4))", + "calc(3em + (25px * (3 / 4)))", + "calc((25px * 3 / 4) + 3em)", + "calc(((25px * 3) / 4) + 3em)", + "calc((25px * (3 / 4)) + 3em)", + "calc(3*25px + 1in)", + "calc(1in - 3em + 2px)", + "calc(1in - (3em + 2px))", + "calc((1in - 3em) + 2px)", + "calc(50px/2)", + "calc(50px/(2 - 1))", + "calc(-3px)", + /* numeric reduction cases */ + "calc(5 * 3 * 2em)", + "calc(2em * 5 * 3)", + "calc((5 * 3) * 2em)", + "calc(2em * (5 * 3))", + "calc((5 + 3) * 2em)", + "calc(2em * (5 + 3))", + "calc(2em / (5 + 3))", + "calc(2em * (5*2 + 3))", + "calc(2em * ((5*2) + 3))", + "calc(2em * (5*(2 + 3)))", + + "calc((5 + 7) * 3em)", + "calc((5em + 3em) - 2em)", + "calc((5em - 3em) + 2em)", + "calc(2em - (5em - 3em))", + "calc(2em + (5em - 3em))", + "calc(2em - (5em + 3em))", + "calc(2em + (5em + 3em))", + "calc(2em + 5em - 3em)", + "calc(2em - 5em - 3em)", + "calc(2em + 5em + 3em)", + "calc(2em - 5em + 3em)", + + "calc(2em / 4 * 3)", + "calc(2em * 4 / 3)", + "calc(2em * 4 * 3)", + "calc(2em / 4 / 3)", + "calc(4 * 2em / 3)", + "calc(4 / 3 * 2em)", + + "calc((2em / 4) * 3)", + "calc((2em * 4) / 3)", + "calc((2em * 4) * 3)", + "calc((2em / 4) / 3)", + "calc((4 * 2em) / 3)", + "calc((4 / 3) * 2em)", + + "calc(2em / (4 * 3))", + "calc(2em * (4 / 3))", + "calc(2em * (4 * 3))", + "calc(2em / (4 / 3))", + "calc(4 * (2em / 3))", + + // Valid cases with unitless zero (which is never + // a length). + "calc(0 * 2em)", + "calc(2em * 0)", + "calc(3em + 0 * 2em)", + "calc(3em + 2em * 0)", + "calc((0 + 2) * 2em)", + "calc((2 + 0) * 2em)", + // And test zero lengths while we're here. + "calc(2 * 0px)", + "calc(0 * 0px)", + "calc(2 * 0em)", + "calc(0 * 0em)", + "calc(0px * 0)", + "calc(0px * 2)", + + ], + invalid_values: [ "20", "-1px", "red", "50%", + /* invalid -moz-calc() values */ + "-moz-calc(2em+ 2px)", + "-moz-calc(2em +2px)", + "-moz-calc(2em+2px)", + "-moz-calc(2em- 2px)", + "-moz-calc(2em -2px)", + "-moz-calc(2em-2px)", + /* invalid calc() values */ + "calc(2em+ 2px)", + "calc(2em +2px)", + "calc(2em+2px)", + "calc(2em- 2px)", + "calc(2em -2px)", + "calc(2em-2px)", + "-moz-min()", + "calc(min())", + "-moz-max()", + "calc(max())", + "-moz-min(5px)", + "calc(min(5px))", + "-moz-max(5px)", + "calc(max(5px))", + "-moz-min(5px,2em)", + "calc(min(5px,2em))", + "-moz-max(5px,2em)", + "calc(max(5px,2em))", + "calc(50px/(2 - 2))", + "calc(5 + 5)", + "calc(5 * 5)", + "calc(5em * 5em)", + "calc(5em / 5em * 5em)", + + "calc(4 * 3 / 2em)", + "calc((4 * 3) / 2em)", + "calc(4 * (3 / 2em))", + "calc(4 / (3 * 2em))", + + // Tests for handling of unitless zero, which cannot + // be a length inside calc(). + "calc(0)", + "calc(0 + 2em)", + "calc(2em + 0)", + "calc(0 * 2)", + "calc(2 * 0)", + "calc(1 * (2em + 0))", + "calc((2em + 0))", + "calc((2em + 0) * 1)", + "calc(1 * (0 + 2em))", + "calc((0 + 2em))", + "calc((0 + 2em) * 1)", + ] + }, + "-moz-column-rule-width": { + domProp: "MozColumnRuleWidth", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-rule-width", + subproperties: [ "column-rule-width" ] + }, + "column-rule-style": { + domProp: "columnRuleStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "solid", "hidden", "ridge", "groove", "inset", "outset", "double", "dotted", "dashed" ], + invalid_values: [ "20", "foo" ] + }, + "-moz-column-rule-style": { + domProp: "MozColumnRuleStyle", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-rule-style", + subproperties: [ "column-rule-style" ] + }, + "column-rule-color": { + domProp: "columnRuleColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "green" }, + initial_values: [ "currentColor" ], + other_values: [ "red", "blue", "#ffff00" ], + invalid_values: [ "ffff00" ] + }, + "-moz-column-rule-color": { + domProp: "MozColumnRuleColor", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-rule-color", + subproperties: [ "column-rule-color" ] + }, + "column-width": { + domProp: "columnWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ + "15px", + "calc(15px)", + "calc(30px - 3em)", + "calc(-15px)", + "0px", + "calc(0px)" + ], + invalid_values: [ "20", "-1px", "50%" ] + }, + "-moz-column-width": { + domProp: "MozColumnWidth", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "column-width", + subproperties: [ "column-width" ] + }, + "-moz-float-edge": { + domProp: "MozFloatEdge", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "content-box" ], + other_values: [ "margin-box" ], + invalid_values: [ "content", "padding", "border", "margin" ] + }, + "-moz-force-broken-image-icon": { + domProp: "MozForceBrokenImageIcon", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0" ], + other_values: [ "1" ], + invalid_values: [] + }, + "-moz-image-region": { + domProp: "MozImageRegion", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "rect(3px 20px 15px 4px)", "rect(17px, 21px, 33px, 2px)" ], + invalid_values: [ "rect(17px, 21px, 33, 2px)" ] + }, + "margin-inline-end": { + domProp: "marginInlineEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* no subproperties */ + /* auto may or may not be initial */ + initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "3em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "5", "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ], + }, + "margin-inline-start": { + domProp: "marginInlineStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* no subproperties */ + /* auto may or may not be initial */ + initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "3em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "5", "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ], + }, + "mask-type": { + domProp: "maskType", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "luminance" ], + other_values: [ "alpha" ], + invalid_values: [], + }, + "-moz-outline-radius": { + domProp: "MozOutlineRadius", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + subproperties: [ "-moz-outline-radius-bottomleft", "-moz-outline-radius-bottomright", "-moz-outline-radius-topleft", "-moz-outline-radius-topright" ], + initial_values: [ "0", "0px", "calc(-2px)", "calc(0px) calc(0pt)", "calc(0px) calc(0em)" ], + other_values: [ "0%", "3%", "1px", "2em", "3em 2px", "2pt 3% 4em", "2px 2px 2px 2px", // circular + "3% / 2%", "1px / 4px", "2em / 1em", "3em 2px / 2px 3em", "2pt 3% 4em / 4pt 1% 5em", "2px 2px 2px 2px / 4px 4px 4px 4px", "1pt / 2pt 3pt", "4pt 5pt / 3pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + "2px 2px calc(2px + 1%) 2px", + "1px 2px 2px 2px / 2px 2px calc(2px + 1%) 2px", + ], + invalid_values: [ "2px -2px", "inherit 2px", "inherit / 2px", "2px inherit", "2px / inherit", "2px 2px 2px 2px 2px", "1px / 2px 2px 2px 2px 2px", "2", "2 2", "2px 2px 2px 2px / 2px 2px 2 2px" ] + }, + "-moz-outline-radius-bottomleft": { + domProp: "MozOutlineRadiusBottomleft", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)", "calc(0px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + }, + "-moz-outline-radius-bottomright": { + domProp: "MozOutlineRadiusBottomright", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)", "calc(0px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + }, + "-moz-outline-radius-topleft": { + domProp: "MozOutlineRadiusTopleft", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)", "calc(0px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + }, + "-moz-outline-radius-topright": { + domProp: "MozOutlineRadiusTopright", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"}, + initial_values: [ "0", "0px", "calc(-2px)", "calc(0px)" ], + other_values: [ "0%", "3%", "1px", "2em", // circular + "3% 2%", "1px 4px", "2em 2pt", // elliptical + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + }, + "padding-inline-end": { + domProp: "paddingInlineEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* no subproperties */ + initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "3em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "5" ] + }, + "padding-inline-start": { + domProp: "paddingInlineStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* no subproperties */ + initial_values: [ "0", "0px", "0%", "0em", "0ex", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "3em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "5" ] + }, + "resize": { + domProp: "resize", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block", "overflow": "auto" }, + initial_values: [ "none" ], + other_values: [ "both", "horizontal", "vertical" ], + invalid_values: [] + }, + "-moz-stack-sizing": { + domProp: "MozStackSizing", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "stretch-to-fit" ], + other_values: [ "ignore" ], + invalid_values: [] + }, + "-moz-tab-size": { + domProp: "MozTabSize", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "8" ], + other_values: [ "0", "3", "99", "12000" ], + invalid_values: [ "-1", "-808", "3.0", "17.5" ] + }, + "-moz-text-size-adjust": { + domProp: "MozTextSizeAdjust", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "none" ], + invalid_values: [ "-5%", "0", "100", "0%", "50%", "100%", "220.3%" ] + }, + "transform": { + domProp: "transform", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "width": "300px", "height": "50px" }, + initial_values: [ "none" ], + other_values: [ "translatex(1px)", "translatex(4em)", + "translatex(-4px)", "translatex(3px)", + "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", + "translatey(4em)", "translate(3px)", "translate(10px, -3px)", + "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", + "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)", + "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", + "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)", + "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)", + "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", + "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", + "translatex(50%)", "translatey(50%)", "translate(50%)", + "translate(3%, 5px)", "translate(5px, 3%)", + "matrix(1, 2, 3, 4, 5, 6)", + /* valid calc() values */ + "translatex(calc(5px + 10%))", + "translatey(calc(0.25 * 5px + 10% / 3))", + "translate(calc(5px - 10% * 3))", + "translate(calc(5px - 3 * 10%), 50px)", + "translate(-50px, calc(5px - 10% * 3))", + "translatez(1px)", "translatez(4em)", "translatez(-4px)", + "translatez(0px)", "translatez(2px) translatez(5px)", + "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)", + "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)", + "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)", + "scalez(-6)", "rotate3d(2, 3, 4, 45deg)", + "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)", + "rotatez(72rad)", "rotatex(0.125turn)", + "perspective(0px)", "perspective(1000px)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)", + ], + invalid_values: ["1px", "#0000ff", "red", "auto", + "translatex(1)", "translatey(1)", "translate(2)", + "translate(-3, -4)", + "translatex(1px 1px)", "translatex(translatex(1px))", + "translatex(#0000ff)", "translatex(red)", "translatey()", + "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)", + "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)", + "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)", + "matrix(0, 1, 2, 3%, 4%, 5%)", "matrix(1, 2, 3, 4, 5px, 6%)", + "matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)", + "matrix(1, 2, 3, 4, 5px, 6em)", + /* invalid calc() values */ + "translatey(-moz-min(5px,10%))", + "translatex(-moz-max(5px,10%))", + "translate(10px, calc(min(5px,10%)))", + "translate(calc(max(5px,10%)), 10%)", + "matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))", + "perspective(-10px)", "matrix3d(dinosaur)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)", + "rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)" + ], + }, + "transform-origin": { + domProp: "transformOrigin", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* no subproperties */ + prerequisites: { "width": "10px", "height": "10px", "display": "block"}, + initial_values: [ "50% 50%", "center", "center center" ], + other_values: [ "25% 25%", "6px 5px", "20% 3em", "0 0", "0in 1in", + "top", "bottom","top left", "top right", + "top center", "center left", "center right", + "bottom left", "bottom right", "bottom center", + "20% center", "6px center", "13in bottom", + "left 50px", "right 13%", "center 40px", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)", + "6px 5px 5px", + "top center 10px" + ], + invalid_values: ["red", "auto", "none", "0.5 0.5", "40px #0000ff", + "border", "center red", "right diagonal", + "#00ffff bottom", "0px calc(0px + rubbish)", + "0px 0px calc(0px + rubbish)"] + }, + "perspective-origin": { + domProp: "perspectiveOrigin", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* no subproperties */ + prerequisites: { "width": "10px", "height": "10px", "display": "block"}, + initial_values: [ "50% 50%", "center", "center center" ], + other_values: [ "25% 25%", "6px 5px", "20% 3em", "0 0", "0in 1in", + "top", "bottom","top left", "top right", + "top center", "center left", "center right", + "bottom left", "bottom right", "bottom center", + "20% center", "6px center", "13in bottom", + "left 50px", "right 13%", "center 40px", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)" ], + invalid_values: [ "red", "auto", "none", "0.5 0.5", "40px #0000ff", + "border", "center red", "right diagonal", + "#00ffff bottom"] + }, + "perspective": { + domProp: "perspective", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "1000px", "500.2px", "0", "0px" ], + invalid_values: [ "pants", "200", "-100px", "-27.2em" ] + }, + "backface-visibility": { + domProp: "backfaceVisibility", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "visible" ], + other_values: [ "hidden" ], + invalid_values: [ "collapse" ] + }, + "transform-style": { + domProp: "transformStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "flat" ], + other_values: [ "preserve-3d" ], + invalid_values: [] + }, + "-moz-user-focus": { + domProp: "MozUserFocus", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "normal", "ignore", "select-all", "select-before", "select-after", "select-same", "select-menu" ], + invalid_values: [] + }, + "-moz-user-input": { + domProp: "MozUserInput", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "none", "enabled", "disabled" ], + invalid_values: [] + }, + "-moz-user-modify": { + domProp: "MozUserModify", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "read-only" ], + other_values: [ "read-write", "write-only" ], + invalid_values: [] + }, + "-moz-user-select": { + domProp: "MozUserSelect", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "none", "text", "element", "elements", "all", "toggle", "tri-state", "-moz-all", "-moz-none" ], + invalid_values: [] + }, + "background": { + domProp: "background", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "background-attachment", "background-color", "background-image", "background-position-x", "background-position-y", "background-repeat", "background-clip", "background-origin", "background-size" ], + initial_values: [ "transparent", "none", "repeat", "scroll", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto", + "transparent none", "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none", + "transparent none repeat scroll top left", "left top repeat none scroll transparent", "transparent none repeat scroll top left / auto", "left top / auto repeat none scroll transparent", "none repeat scroll 0% 0% / auto auto transparent", + "padding-box border-box" ], + other_values: [ + /* without multiple backgrounds */ + "green", + "none green repeat scroll left top", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') transparent left top scroll", + "repeat-x", + "repeat-y", + "no-repeat", + "none repeat-y transparent scroll 0% 0%", + "fixed", + "0% top transparent fixed repeat none", + "top", + "left", + "50% 50%", + "center", + "top / 100px", + "left / contain", + "left / cover", + "10px / 10%", + "10em / calc(20px)", + "top left / 100px 100px", + "top left / 100px auto", + "top left / 100px 10%", + "top left / 100px calc(20px)", + "bottom right scroll none transparent repeat", + "50% transparent", + "transparent 50%", + "50%", + "-moz-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat", + "-moz-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10px -0.125turn, red, blue) repeat", + "-moz-repeating-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat", + "-moz-repeating-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-element(#test) lime", + /* multiple backgrounds */ + "url(404.png), url(404.png)", + "url(404.png), url(404.png) transparent", + "url(404.png), url(404.png) red", + "repeat-x, fixed, none", + "0% top url(404.png), url(404.png) 0% top", + "fixed repeat-y top left url(404.png), repeat-x green", + "url(404.png), -moz-linear-gradient(20px 20px -45deg, blue, green), -moz-element(#a) black", + "top left / contain, bottom right / cover", + /* test cases with clip+origin in the shorthand */ + "url(404.png) green padding-box", + "url(404.png) border-box transparent", + "content-box url(404.png) blue", + "url(404.png) green padding-box padding-box", + "url(404.png) green padding-box border-box", + "content-box border-box url(404.png) blue", + ], + invalid_values: [ + /* mixes with keywords have to be in correct order */ + "50% left", "top 50%", + /* no quirks mode colors */ + "-moz-radial-gradient(10% bottom, ffffff, black) scroll no-repeat", + /* no quirks mode lengths */ + "-moz-linear-gradient(10 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat", + "linear-gradient(red -99, yellow, green, blue 120%)", + /* bug 258080: don't accept background-position separated */ + "left url(404.png) top", "top url(404.png) left", + /* not allowed to have color in non-bottom layer */ + "url(404.png) transparent, url(404.png)", + "url(404.png) red, url(404.png)", + "url(404.png) transparent, url(404.png) transparent", + "url(404.png) transparent red, url(404.png) transparent red", + "url(404.png) red, url(404.png) red", + "url(404.png) rgba(0, 0, 0, 0), url(404.png)", + "url(404.png) rgb(255, 0, 0), url(404.png)", + "url(404.png) rgba(0, 0, 0, 0), url(404.png) rgba(0, 0, 0, 0)", + "url(404.png) rgba(0, 0, 0, 0) rgb(255, 0, 0), url(404.png) rgba(0, 0, 0, 0) rgb(255, 0, 0)", + "url(404.png) rgb(255, 0, 0), url(404.png) rgb(255, 0, 0)", + /* bug 513395: old syntax for gradients */ + "-moz-radial-gradient(10% bottom, 30px, 20px 20px, 10px, from(#ffffff), to(black)) scroll no-repeat", + "-moz-linear-gradient(10px 10px, 20px 20px, from(red), to(blue)) repeat", + /* clip and origin separated in the shorthand */ + "url(404.png) padding-box green border-box", + "url(404.png) padding-box green padding-box", + "transparent padding-box url(404.png) border-box", + "transparent padding-box url(404.png) padding-box", + /* error inside functions */ + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), rubbish, 50%, 30%, 0) transparent", + "-moz-element(#a rubbish) black", + "text", + "text border-box", + "content-box text text", + "padding-box text url(404.png) text", + ] + }, + "background-attachment": { + domProp: "backgroundAttachment", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "scroll" ], + other_values: [ "fixed", "local", "scroll,scroll", "fixed, scroll", "scroll, fixed, local, scroll", "fixed, fixed" ], + invalid_values: [] + }, + "background-clip": { + /* + * When we rename this to 'background-clip', we also + * need to rename the values to match the spec. + */ + domProp: "backgroundClip", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "border-box" ], + other_values: [ "content-box", "padding-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "border-box border-box" ] + }, + "background-color": { + domProp: "backgroundColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "transparent", "rgba(255, 127, 15, 0)", "hsla(240, 97%, 50%, 0.0)", "rgba(0, 0, 0, 0)", "rgba(255,255,255,-3.7)" ], + other_values: [ "green", "rgb(255, 0, 128)", "#fc2", "#96ed2a", "black", "rgba(255,255,0,3)", "hsl(240, 50%, 50%)", "rgb(50%, 50%, 50%)", "-moz-default-background-color", "rgb(100, 100.0, 100)" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "rgb(100, 100%, 100)" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "background-image": { + domProp: "backgroundImage", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==')", 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + "none, none", + "none, none, none, none, none", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "none, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + ].concat(validGradientAndElementValues), + invalid_values: [ + ].concat(invalidGradientAndElementValues), + unbalanced_values: [ + ].concat(unbalancedGradientAndElementValues) + }, + "background-origin": { + domProp: "backgroundOrigin", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "padding-box" ], + other_values: [ "border-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "padding-box padding-box" ] + }, + "background-position": { + domProp: "backgroundPosition", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + initial_values: [ "top 0% left 0%", "top 0% left", "top left", "left top", "0% 0%", "0% top", "left 0%" ], + other_values: [ "top", "left", "right", "bottom", "center", "center bottom", "bottom center", "center right", "right center", "center top", "top center", "center left", "left center", "right bottom", "bottom right", "50%", "top left, top left", "top left, top right", "top right, top left", "left top, 0% 0%", "10% 20%, 30%, 40%", "top left, bottom right", "right bottom, left top", "0%", "0px", "30px", "0%, 10%, 20%, 30%", "top, top, top, top, top", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)", + "0px 0px", + "right 20px top 60px", + "right 20px bottom 60px", + "left 20px top 60px", + "left 20px bottom 60px", + "right -50px top -50px", + "left -50px bottom -50px", + "right 20px top -50px", + "right -20px top 50px", + "right 3em bottom 10px", + "bottom 3em right 10px", + "top 3em right 10px", + "left 15px", + "10px top", + "left top 15px", + "left 10px top", + "left 20%", + "right 20%" + ], + subproperties: [ "background-position-x", "background-position-y" ], + invalid_values: [ "center 10px center 4px", "center 10px center", + "top 20%", "bottom 20%", "50% left", "top 50%", + "50% bottom 10%", "right 10% 50%", "left right", + "top bottom", "left 10% right", + "top 20px bottom 20px", "left left", + "0px calc(0px + rubbish)"], + quirks_values: { + "20 20": "20px 20px", + "10 5px": "10px 5px", + "7px 2": "7px 2px", + }, + }, + "background-position-x": { + domProp: "backgroundPositionX", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "left 0%", "left", "0%" ], + other_values: [ "right", "center", "50%", "left, left", "left, right", "right, left", "left, 0%", "10%, 20%, 40%", "0px", "30px", "0%, 10%, 20%, 30%", "left, left, left, left, left", + "calc(20px)", + "calc(20px + 1em)", + "calc(20px / 2)", + "calc(20px + 50%)", + "calc(50% - 10px)", + "calc(-20px)", + "calc(-50%)", + "calc(-20%)", + "right 20px", + "left 20px", + "right -50px", + "left -50px", + "right 20px", + "right 3em", + ], + invalid_values: [ "center 10px", "right 10% 50%", "left right", "left left", + "bottom 20px", "top 10%", "bottom 3em", + "top", "bottom", "top, top", "top, bottom", "bottom, top", "top, 0%", "top, top, top, top, top", + "calc(0px + rubbish)"], + }, + "background-position-y": { + domProp: "backgroundPositionY", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "top 0%", "top", "0%" ], + other_values: [ "bottom", "center", "50%", "top, top", "top, bottom", "bottom, top", "top, 0%", "10%, 20%, 40%", "0px", "30px", "0%, 10%, 20%, 30%", "top, top, top, top, top", + "calc(20px)", + "calc(20px + 1em)", + "calc(20px / 2)", + "calc(20px + 50%)", + "calc(50% - 10px)", + "calc(-20px)", + "calc(-50%)", + "calc(-20%)", + "bottom 20px", + "top 20px", + "bottom -50px", + "top -50px", + "bottom 20px", + "bottom 3em", + ], + invalid_values: [ "center 10px", "bottom 10% 50%", "top bottom", "top top", + "right 20px", "left 10%", "right 3em", + "left", "right", "left, left", "left, right", "right, left", "left, 0%", "left, left, left, left, left", + "calc(0px + rubbish)"], + }, + "background-repeat": { + domProp: "backgroundRepeat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "repeat", "repeat repeat" ], + other_values: [ "repeat-x", "repeat-y", "no-repeat", + "repeat-x, repeat-x", + "repeat, no-repeat", + "repeat-y, no-repeat, repeat-y", + "repeat, repeat, repeat", + "repeat no-repeat", + "no-repeat repeat", + "no-repeat no-repeat", + "repeat repeat, repeat repeat", + "round, repeat", + "round repeat, repeat-x", + "round no-repeat, repeat-y", + "round round", + "space, repeat", + "space repeat, repeat-x", + "space no-repeat, repeat-y", + "space space", + "space round" + ], + invalid_values: [ "repeat repeat repeat", + "repeat-x repeat-y", + "repeat repeat-x", + "repeat repeat-y", + "repeat-x repeat", + "repeat-y repeat", + "round round round", + "repeat-x round", + "round repeat-x", + "repeat-y round", + "round repeat-y", + "space space space", + "repeat-x space", + "space repeat-x", + "repeat-y space", + "space repeat-y" ] + }, + "background-size": { + domProp: "backgroundSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto", "auto auto" ], + other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)" + ], + invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ] + }, + "border": { + domProp: "border", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left-color", "border-left-style", "border-left-width", "border-right-color", "border-right-style", "border-right-width", "border-top-color", "border-top-style", "border-top-width", "-moz-border-top-colors", "-moz-border-right-colors", "-moz-border-bottom-colors", "-moz-border-left-colors", "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor", "calc(4px - 1px) none" ], + other_values: [ "solid", "medium solid", "green solid", "10px solid", "thick solid", "calc(2px) solid blue" ], + invalid_values: [ "5%", "medium solid ff00ff", "5 solid green" ] + }, + "border-bottom": { + domProp: "borderBottom", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-bottom-color", "border-bottom-style", "border-bottom-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "border-bottom-color": { + domProp: "borderBottomColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "border-bottom-style": { + domProp: "borderBottomStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-bottom-width": { + domProp: "borderBottomWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "border-bottom-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%" ], + quirks_values: { "5": "5px" }, + }, + "border-collapse": { + domProp: "borderCollapse", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "separate" ], + other_values: [ "collapse" ], + invalid_values: [] + }, + "border-color": { + domProp: "borderColor", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-top-color", "border-right-color", "border-bottom-color", "border-left-color" ], + initial_values: [ "currentColor", "currentColor currentColor", "currentColor currentColor currentColor", "currentColor currentColor currentcolor CURRENTcolor" ], + other_values: [ "green", "currentColor green", "currentColor currentColor green", "currentColor currentColor currentColor green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "red rgb(nonsense)", "red 1px" ], + unbalanced_values: [ "red rgb(" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "border-left": { + domProp: "borderLeft", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-left-color", "border-left-style", "border-left-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green", "calc(5px + rubbish) green solid", "5px rgb(0, rubbish, 0) solid" ] + }, + "border-left-color": { + domProp: "borderLeftColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "border-left-style": { + domProp: "borderLeftStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-left-width": { + domProp: "borderLeftWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "border-left-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%" ], + quirks_values: { "5": "5px" }, + }, + "border-right": { + domProp: "borderRight", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-right-color", "border-right-style", "border-right-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "border-right-color": { + domProp: "borderRightColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "border-right-style": { + domProp: "borderRightStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-right-width": { + domProp: "borderRightWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "border-right-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%" ], + quirks_values: { "5": "5px" }, + }, + "border-spacing": { + domProp: "borderSpacing", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0 0", "0px", "0 0px", "calc(0px)", "calc(0px) calc(0em)", "calc(2em - 2em) calc(3px + 7px - 10px)", "calc(-5px)", "calc(-5px) calc(-5px)" ], + other_values: [ "3px", "4em 2px", "4em 0", "0px 2px", "calc(7px)", "0 calc(7px)", "calc(7px) 0", "calc(0px) calc(7px)", "calc(7px) calc(0px)", "7px calc(0px)", "calc(0px) 7px", "7px calc(0px)", "3px calc(2em)" ], + invalid_values: [ "0%", "0 0%", "-5px", "-5px -5px", "0 -5px", "-5px 0", "0 calc(0px + rubbish)" ], + quirks_values: { + "2px 5": "2px 5px", + "7": "7px", + "3 4px": "3px 4px", + }, + }, + "border-style": { + domProp: "borderStyle", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-top-style", "border-right-style", "border-bottom-style", "border-left-style" ], + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none", "none none", "none none none", "none none none none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge", "none solid", "none none solid", "none none none solid", "groove none none none", "none ridge none none", "none none double none", "none none none dotted" ], + invalid_values: [] + }, + "border-top": { + domProp: "borderTop", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-top-color", "border-top-style", "border-top-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "border-top-color": { + domProp: "borderTopColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000" ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a" }, + }, + "border-top-style": { + domProp: "borderTopStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-top-width": { + domProp: "borderTopWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "border-top-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%" ], + quirks_values: { "5": "5px" }, + }, + "border-width": { + domProp: "borderWidth", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-top-width", "border-right-width", "border-bottom-width", "border-left-width" ], + prerequisites: { "border-style": "solid" }, + initial_values: [ "medium", "3px", "medium medium", "3px medium medium", "medium 3px medium medium", "calc(3px) 3px calc(5px - 2px) calc(2px - -1px)" ], + other_values: [ "thin", "thick", "1px", "2em", "2px 0 0px 1em", "calc(2em)" ], + invalid_values: [ "5%", "1px calc(nonsense)", "1px red" ], + unbalanced_values: [ "1px calc(" ], + quirks_values: { "5": "5px" }, + }, + "bottom": { + domProp: "bottom", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "box-shadow": { + domProp: "boxShadow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + prerequisites: { "color": "blue" }, + other_values: [ "2px 2px", "2px 2px 1px", "2px 2px 2px 2px", "blue 3px 2px", "2px 2px 1px 5px green", "2px 2px red", "green 2px 2px 1px", "green 2px 2px, blue 1px 3px 4px", "currentColor 3px 3px", "blue 2px 2px, currentColor 1px 2px, 1px 2px 3px 2px orange", "3px 0 0 0", "inset 2px 2px 3px 4px black", "2px -2px green inset, 4px 4px 3px blue, inset 2px 2px", + /* calc() values */ + "2px 2px calc(-5px)", /* clamped */ + "calc(3em - 2px) 2px green", + "green calc(3em - 2px) 2px", + "2px calc(2px + 0.2em)", + "blue 2px calc(2px + 0.2em)", + "2px calc(2px + 0.2em) blue", + "calc(-2px) calc(-2px)", + "-2px -2px", + "calc(2px) calc(2px)", + "calc(2px) calc(2px) calc(2px)", + "calc(2px) calc(2px) calc(2px) calc(2px)" + ], + invalid_values: [ "3% 3%", "1px 1px 1px 1px 1px", "2px 2px, none", "red 2px 2px blue", "inherit, 2px 2px", "2px 2px, inherit", "2px 2px -5px", "inset 4px 4px black inset", "inset inherit", "inset none", "3 3", "3px 3", "3 3px", "3px 3px 3", "3px 3px 3px 3", "3px calc(3px + rubbish)", "3px 3px calc(3px + rubbish)", "3px 3px 3px calc(3px + rubbish)", "3px 3px 3px 3px rgb(0, rubbish, 0)" ] + }, + "caption-side": { + domProp: "captionSide", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "top" ], + other_values: [ "bottom", "left", "right", "top-outside", "bottom-outside" ], + invalid_values: [] + }, + "clear": { + domProp: "clear", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "left", "right", "both" ], + invalid_values: [] + }, + "clip": { + domProp: "clip", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "rect(0 0 0 0)", "rect(auto,auto,auto,auto)", "rect(3px, 4px, 4em, 0)", "rect(auto, 3em, 4pt, 2px)", "rect(2px 3px 4px 5px)" ], + invalid_values: [ "rect(auto, 3em, 2%, 5px)" ], + quirks_values: { "rect(1, 2, 3, 4)": "rect(1px, 2px, 3px, 4px)" }, + }, + "color": { + domProp: "color", + inherited: true, + type: CSS_TYPE_LONGHAND, + /* XXX should test currentColor, but may or may not be initial */ + initial_values: [ "black", "#000", "#000f", "#000000ff", "-moz-default-color", "rgb(0, 0, 0)", "rgb(0%, 0%, 0%)", + /* css-color-4: */ + /* rgb() and rgba() are aliases of each other. */ + "rgb(0, 0, 0)", "rgba(0, 0, 0)", "rgb(0, 0, 0, 1)", "rgba(0, 0, 0, 1)", + /* hsl() and hsla() are aliases of each other. */ + "hsl(0, 0%, 0%)", "hsla(0, 0%, 0%)", "hsl(0, 0%, 0%, 1)", "hsla(0, 0%, 0%, 1)", + /* rgb() and rgba() functions now accept rather than . */ + "rgb(0.0, 0.0, 0.0)", "rgba(0.0, 0.0, 0.0)", "rgb(0.0, 0.0, 0.0, 1)", "rgba(0.0, 0.0, 0.0, 1)", + /* now accepts as well as in rgba() and hsla(). */ + "rgb(0.0, 0.0, 0.0, 100%)", "hsl(0, 0%, 0%, 100%)", + /* rgb() and hsl() now support comma-less expression. */ + "rgb(0 0 0)", "rgb(0 0 0 / 1)", "rgb(0/* comment */0/* comment */0)", "rgb(0/* comment */0/* comment*/0/1.0)", + "hsl(0 0% 0%)", "hsl(0 0% 0% / 1)", "hsl(0/* comment */0%/* comment */0%)", "hsl(0/* comment */0%/* comment */0%/1)", + /* Support for hsl() hue component. */ + "hsl(0deg, 0%, 0%)", "hsl(360deg, 0%, 0%)", "hsl(0grad, 0%, 0%)", "hsl(400grad, 0%, 0%)", "hsl(0rad, 0%, 0%)", "hsl(0turn, 0%, 0%)", "hsl(1turn, 0%, 0%)", + ], + other_values: [ "green", "#f3c", "#fed292", "rgba(45,300,12,2)", "transparent", "-moz-nativehyperlinktext", "rgba(255,128,0,0.5)", "#e0fc", "#10fcee72", + /* css-color-4: */ + "rgb(100, 100.0, 100)", "rgb(300 300 300 / 200%)", "rgb(300.0 300.0 300.0 / 2.0)", "hsl(720, 200%, 200%, 2.0)", "hsla(720 200% 200% / 200%)", + "hsl(480deg, 20%, 30%, 0.3)", "hsl(55grad, 400%, 30%)", "hsl(0.5grad 400% 500% / 9.0)", "hsl(33rad 100% 90% / 4)", "hsl(0.33turn, 40%, 40%, 10%)", + ], + invalid_values: [ "#f", "#ff", "#fffff", "#fffffff", "#fffffffff", + "rgb(100%, 0, 100%)", "rgba(100, 0, 100%, 30%)", + "hsl(0, 0, 0%)", "hsla(0%, 0%, 0%, 0.1)", + /* trailing commas */ + "rgb(0, 0, 0,)", "rgba(0, 0, 0, 0,)", + "hsl(0, 0%, 0%,)", "hsla(0, 0%, 0%, 1,)", + /* css-color-4: */ + /* comma and comma-less expressions should not mix together. */ + "rgb(0, 0, 0 / 1)", "rgb(0 0 0, 1)", "rgb(0, 0 0, 1)", "rgb(0 0, 0 / 1)", + "hsl(0, 0%, 0% / 1)", "hsl(0 0% 0%, 1)", "hsl(0 0% 0%, 1)", "hsl(0 0%, 0% / 1)", + /* trailing slash */ + "rgb(0 0 0 /)", "rgb(0, 0, 0 /)", + "hsl(0 0% 0% /)", "hsl(0, 0%, 0% /)", + ], + quirks_values: { "000000": "#000000", "96ed2a": "#96ed2a", "fff": "#ffffff", "ffffff": "#ffffff", }, + }, + "content": { + domProp: "content", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX needs to be on pseudo-elements */ + initial_values: [ "normal", "none" ], + other_values: [ '""', "''", '"hello"', "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==')", 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', 'counter(foo)', 'counter(bar, upper-roman)', 'counters(foo, ".")', "counters(bar, '-', lower-greek)", "'-' counter(foo) '.'", "attr(title)", "open-quote", "close-quote", "no-open-quote", "no-close-quote", "close-quote attr(title) counters(foo, '.', upper-alpha)", "counter(foo, none)", "counters(bar, '.', none)", "attr(\\32)", "attr(\\2)", "attr(-\\2)", "attr(-\\32)", "counter(\\2)", "counters(\\32, '.')", "counter(-\\32, upper-roman)", "counters(-\\2, '-', lower-greek)", "counter(\\()", "counters(a\\+b, '.')", "counter(\\}, upper-alpha)", "-moz-alt-content", "counter(foo, symbols('*'))", "counter(foo, symbols(numeric '0' '1'))", "counters(foo, '.', symbols('*'))", "counters(foo, '.', symbols(numeric '0' '1'))" ], + invalid_values: [ 'counters(foo)', 'counter(foo, ".")', 'attr("title")', "attr('title')", "attr(2)", "attr(-2)", "counter(2)", "counters(-2, '.')", "-moz-alt-content 'foo'", "'foo' -moz-alt-content", "counter(one, two, three) 'foo'" ] + }, + "counter-increment": { + domProp: "counterIncrement", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "foo 1", "bar", "foo 3 bar baz 2", "\\32 1", "-\\32 1", "-c 1", "\\32 1", "-\\32 1", "\\2 1", "-\\2 1", "-c 1", "\\2 1", "-\\2 1", "-\\7f \\9e 1" ], + invalid_values: [ "none foo", "none foo 3", "foo none", "foo 3 none" ], + unbalanced_values: [ "foo 1 (" ] + }, + "counter-reset": { + domProp: "counterReset", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "foo 1", "bar", "foo 3 bar baz 2", "\\32 1", "-\\32 1", "-c 1", "\\32 1", "-\\32 1", "\\2 1", "-\\2 1", "-c 1", "\\2 1", "-\\2 1", "-\\7f \\9e 1" ], + invalid_values: [ "none foo", "none foo 3", "foo none", "foo 3 none" ] + }, + "cursor": { + domProp: "cursor", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "crosshair", "default", "pointer", "move", "e-resize", "ne-resize", "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize", "w-resize", "text", "wait", "help", "progress", "copy", "alias", "context-menu", "cell", "not-allowed", "col-resize", "row-resize", "no-drop", "vertical-text", "all-scroll", "nesw-resize", "nwse-resize", "ns-resize", "ew-resize", "none", "grab", "grabbing", "zoom-in", "zoom-out", "-moz-grab", "-moz-grabbing", "-moz-zoom-in", "-moz-zoom-out", "url(foo.png), move", "url(foo.png) 5 7, move", "url(foo.png) 12 3, url(bar.png), no-drop", "url(foo.png), url(bar.png) 7 2, wait", "url(foo.png) 3 2, url(bar.png) 7 9, pointer" ], + invalid_values: [ "url(foo.png)", "url(foo.png) 5 5" ] + }, + "direction": { + domProp: "direction", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "ltr" ], + other_values: [ "rtl" ], + invalid_values: [] + }, + "display": { + domProp: "display", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "inline" ], + /* XXX none will really mess with other properties */ + prerequisites: { "float": "none", "position": "static", "contain": "none" }, + other_values: [ + "block", + "flex", + "inline-flex", + "list-item", + "inline-block", + "table", + "inline-table", + "table-row-group", + "table-header-group", + "table-footer-group", + "table-row", + "table-column-group", + "table-column", + "table-cell", + "table-caption", + "ruby", + "ruby-base", + "ruby-base-container", + "ruby-text", + "ruby-text-container", + "none" + ], + invalid_values: [] + }, + "empty-cells": { + domProp: "emptyCells", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "show" ], + other_values: [ "hide" ], + invalid_values: [] + }, + "float": { + domProp: "cssFloat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "left", "right" ], + invalid_values: [] + }, + "font": { + domProp: "font", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "writing-mode": "initial" }, + subproperties: [ "font-style", "font-variant", "font-weight", "font-size", "line-height", "font-family", "font-stretch", + "font-size-adjust", "font-feature-settings", "font-language-override", + "font-kerning", "font-synthesis", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", + "font-variant-ligatures", "font-variant-numeric", "font-variant-position" ], + initial_values: [ (gInitialFontFamilyIsSansSerif ? "medium sans-serif" : "medium serif") ], + other_values: [ "large serif", "9px fantasy", "condensed bold italic small-caps 24px/1.4 Times New Roman, serif", "small inherit roman", "small roman inherit", + // system fonts + "caption", "icon", "menu", "message-box", "small-caption", "status-bar", + // Gecko-specific system fonts + "-moz-window", "-moz-document", "-moz-desktop", "-moz-info", "-moz-dialog", "-moz-button", "-moz-pull-down-menu", "-moz-list", "-moz-field", "-moz-workspace", + // line-height with calc() + "condensed bold italic small-caps 24px/calc(2px) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(50%) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(3*25px) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(25px*3) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(3*25px + 50%) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(1 + 2*3/4) Times New Roman, serif", + ], + invalid_values: [ "9 fantasy", "-2px fantasy", + // line-height with calc() + "condensed bold italic small-caps 24px/calc(1 + 2px) Times New Roman, serif", + "condensed bold italic small-caps 24px/calc(100% + 0.1) Times New Roman, serif", + ] + }, + "font-family": { + domProp: "fontFamily", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ (gInitialFontFamilyIsSansSerif ? "sans-serif" : "serif") ], + other_values: [ (gInitialFontFamilyIsSansSerif ? "serif" : "sans-serif"), "Times New Roman, serif", "'Times New Roman', serif", "cursive", "fantasy", "\\\"Times New Roman", "\"Times New Roman\"", "Times, \\\"Times New Roman", "Times, \"Times New Roman\"", "-no-such-font-installed", "inherit roman", "roman inherit", "Times, inherit roman", "inherit roman, Times", "roman inherit, Times", "Times, roman inherit" ], + invalid_values: [ "\"Times New\" Roman", "\"Times New Roman\n", "Times, \"Times New Roman\n" ] + }, + "font-feature-settings": { + domProp: "fontFeatureSettings", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ + "'liga' on", "'liga'", "\"liga\" 1", "'liga', 'clig' 1", + "\"liga\" off", "\"liga\" 0", '"cv01" 3, "cv02" 4', + '"cswh", "smcp" off, "salt" 4', '"cswh" 1, "smcp" off, "salt" 4', + '"cswh" 0, \'blah\', "liga", "smcp" off, "salt" 4', + '"liga" ,"smcp" 0 , "blah"' + ], + invalid_values: [ + 'liga', 'liga 1', 'liga normal', '"liga" normal', 'normal liga', + 'normal "liga"', 'normal, "liga"', '"liga=1"', "'foobar' on", + '"blahblah" 0', '"liga" 3.14', '"liga" 1 3.14', '"liga" 1 normal', + '"liga" 1 off', '"liga" on off', '"liga" , 0 "smcp"', '"liga" "smcp"' + ] + }, + "font-kerning": { + domProp: "fontKerning", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "normal", "none" ], + invalid_values: [ "on" ] + }, + "font-language-override": { + domProp: "fontLanguageOverride", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "'ENG'", "'TRK'", "\"TRK\"", "'N\\'Ko'" ], + invalid_values: [ "TRK", "ja" ] + }, + "font-size": { + domProp: "fontSize", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "medium", + "1rem", + "calc(1rem)", + "calc(0.75rem + 200% - 125% + 0.25rem - 75%)" + ], + other_values: [ "large", "2em", "50%", "xx-small", "36pt", "8px", "larger", "smaller", + "0px", + "0%", + "calc(2em)", + "calc(36pt + 75% + (30% + 2em + 2px))", + "calc(-2em)", + "calc(-50%)", + "calc(-1px)" + ], + invalid_values: [ "-2em", "-50%", "-1px" ], + quirks_values: { "5": "5px" }, + }, + "font-size-adjust": { + domProp: "fontSizeAdjust", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "0.3", "0.5", "0.7", "0.0", "0", "3" ], + invalid_values: [ "-0.3", "-1" ] + }, + "font-stretch": { + domProp: "fontStretch", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" ], + invalid_values: [ "narrower", "wider" ] + }, + "font-style": { + domProp: "fontStyle", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "italic", "oblique" ], + invalid_values: [] + }, + "font-synthesis": { + domProp: "fontSynthesis", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "weight style" ], + other_values: [ "none", "weight", "style" ], + invalid_values: [ "weight none", "style none", "none style", "weight 10px", "weight weight", "style style" ] + }, + "font-variant": { + domProp: "fontVariant", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position" ], + initial_values: [ "normal" ], + other_values: [ "small-caps", "none", "traditional oldstyle-nums", "all-small-caps", "common-ligatures no-discretionary-ligatures", + "proportional-nums oldstyle-nums", "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal", + "traditional historical-forms styleset(ok-alt-a, ok-alt-b)", "styleset(potato)" ], + invalid_values: [ "small-caps normal", "small-caps small-caps", "none common-ligatures", "common-ligatures none", "small-caps potato", + "small-caps jis83 all-small-caps", "super historical-ligatures sub", "stacked-fractions diagonal-fractions historical-ligatures", + "common-ligatures traditional common-ligatures", "lining-nums traditional slashed-zero ordinal normal", + "traditional historical-forms styleset(ok-alt-a, ok-alt-b) historical-forms", + "historical-forms styleset(ok-alt-a, ok-alt-b) traditional styleset(potato)", "annotation(a,b,c)" ] + }, + "font-variant-alternates": { + domProp: "fontVariantAlternates", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "historical-forms", + "styleset(alt-a, alt-b)", "character-variant(a, b, c)", "annotation(circled)", + "swash(squishy)", "styleset(complex\\ blob, a)", "annotation(\\62 lah)" ], + invalid_values: [ "historical-forms normal", "historical-forms historical-forms", + "swash", "swash(3)", "annotation(a, b)", "ornaments(a,b)", + "styleset(1234blah)", "annotation(a), annotation(b)", "annotation(a) normal" ] + }, + "font-variant-caps": { + domProp: "fontVariantCaps", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "titling-caps", "unicase" ], + invalid_values: [ "normal small-caps", "petite-caps normal", "unicase unicase" ] + }, + "font-variant-east-asian": { + domProp: "fontVariantEastAsian", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "jis78", "jis83", "jis90", "jis04", "simplified", "traditional", "full-width", "proportional-width", "ruby", + "jis78 full-width", "jis78 full-width ruby", "simplified proportional-width", "ruby simplified" ], + invalid_values: [ "jis78 normal", "jis90 jis04", "simplified traditional", "full-width proportional-width", + "ruby simplified ruby", "jis78 ruby simplified" ] + }, + "font-variant-ligatures": { + domProp: "fontVariantLigatures", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "none", "common-ligatures", "no-common-ligatures", "discretionary-ligatures", "no-discretionary-ligatures", + "historical-ligatures", "no-historical-ligatures", "contextual", "no-contextual", + "common-ligatures no-discretionary-ligatures", "contextual no-discretionary-ligatures", + "historical-ligatures no-common-ligatures", "no-historical-ligatures discretionary-ligatures", + "common-ligatures no-discretionary-ligatures historical-ligatures no-contextual" ], + invalid_values: [ "common-ligatures normal", "common-ligatures no-common-ligatures", "common-ligatures common-ligatures", + "no-historical-ligatures historical-ligatures", "no-discretionary-ligatures discretionary-ligatures", + "no-contextual contextual", "common-ligatures no-discretionary-ligatures no-common-ligatures", + "common-ligatures none", "no-discretionary-ligatures none", "none common-ligatures" ] + }, + "font-variant-numeric": { + domProp: "fontVariantNumeric", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "lining-nums", "oldstyle-nums", "proportional-nums", "tabular-nums", "diagonal-fractions", + "stacked-fractions", "slashed-zero", "ordinal", "lining-nums diagonal-fractions", + "tabular-nums stacked-fractions", "tabular-nums slashed-zero stacked-fractions", + "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal" ], + invalid_values: [ "lining-nums normal", "lining-nums oldstyle-nums", "lining-nums normal slashed-zero ordinal", + "proportional-nums tabular-nums", "diagonal-fractions stacked-fractions", "slashed-zero diagonal-fractions slashed-zero", + "lining-nums slashed-zero diagonal-fractions oldstyle-nums", "diagonal-fractions diagonal-fractions" ] + }, + "font-variant-position": { + domProp: "fontVariantPosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "super", "sub" ], + invalid_values: [ "normal sub", "super sub" ] + }, + "font-weight": { + domProp: "fontWeight", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal", "400" ], + other_values: [ "bold", "100", "200", "300", "500", "600", "700", "800", "900", "bolder", "lighter" ], + invalid_values: [ "0", "100.0", "107", "399", "401", "699", "710", "1000" ] + }, + "height": { + domProp: "height", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* FIXME: test zero, and test calc clamping */ + initial_values: [ " auto", + // these four keywords compute to the initial value when the + // writing mode is horizontal, and that's the context we're testing in + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + ], + /* computed value tests for height test more with display:block */ + prerequisites: { "display": "block" }, + other_values: [ "15px", "3em", "15%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none" ], + quirks_values: { "5": "5px" }, + }, + "ime-mode": { + domProp: "imeMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "normal", "disabled", "active", "inactive" ], + invalid_values: [ "none", "enabled", "1px" ] + }, + "left": { + domProp: "left", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "letter-spacing": { + domProp: "letterSpacing", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "0", "0px", "1em", "2px", "-3px", + "calc(0px)", "calc(1em)", "calc(1em + 3px)", + "calc(15px / 2)", "calc(15px/2)", "calc(-3px)" + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "line-height": { + domProp: "lineHeight", + inherited: true, + type: CSS_TYPE_LONGHAND, + /* + * Inheritance tests require consistent font size, since + * getComputedStyle (which uses the CSS2 computed value, or + * CSS2.1 used value) doesn't match what the CSS2.1 computed + * value is. And they even require consistent font metrics for + * computation of 'normal'. -moz-block-height requires height + * on a block. + */ + prerequisites: { "font-size": "19px", "font-size-adjust": "none", "font-family": "serif", "font-weight": "normal", "font-style": "normal", "height": "18px", "display": "block", "writing-mode": "initial" }, + initial_values: [ "normal" ], + other_values: [ "1.0", "1", "1em", "47px", "-moz-block-height", "calc(2px)", "calc(50%)", "calc(3*25px)", "calc(25px*3)", "calc(3*25px + 50%)", "calc(1 + 2*3/4)" ], + invalid_values: [ "calc(1 + 2px)", "calc(100% + 0.1)" ] + }, + "list-style": { + domProp: "listStyle", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "list-style-type", "list-style-position", "list-style-image" ], + initial_values: [ "outside", "disc", "disc outside", "outside disc", "disc none", "none disc", "none disc outside", "none outside disc", "disc none outside", "disc outside none", "outside none disc", "outside disc none" ], + other_values: [ "inside none", "none inside", "none none inside", "square", "none", "none none", "outside none none", "none outside none", "none none outside", "none outside", "outside none", "outside outside", "outside inside", "\\32 style", "\\32 style inside", + '"-"', "'-'", "inside '-'", "'-' outside", "none '-'", "inside none '-'", + "symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + "symbols(cyclic \"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + "inside symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + "symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\") outside", + "none symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + "inside none symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + 'none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none', + 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside', + 'outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + 'outside none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + 'outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none', + 'none url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside', + 'none outside url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") outside none', + 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==") none outside' + ], + invalid_values: [ "disc disc", "unknown value", "none none none", "none disc url(404.png)", "none url(404.png) disc", "disc none url(404.png)", "disc url(404.png) none", "url(404.png) none disc", "url(404.png) disc none", "none disc outside url(404.png)" ] + }, + "list-style-image": { + domProp: "listStyleImage", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + // Add some tests for interesting url() values here to test serialization, etc. + "url(\'data:text/plain,\"\')", + "url(\"data:text/plain,\'\")", + "url(\'data:text/plain,\\\'\')", + "url(\"data:text/plain,\\\"\")", + "url(\'data:text/plain,\\\"\')", + "url(\"data:text/plain,\\\'\")", + "url(data:text/plain,\\\\)", + ], + invalid_values: [] + }, + "list-style-position": { + domProp: "listStylePosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "outside" ], + other_values: [ "inside" ], + invalid_values: [] + }, + "list-style-type": { + domProp: "listStyleType", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "disc" ], + other_values: [ "none", "circle", "square", + "disclosure-closed", "disclosure-open", + "decimal", "decimal-leading-zero", + "lower-roman", "upper-roman", "lower-greek", + "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", + "hebrew", "armenian", "georgian", + "cjk-decimal", "cjk-ideographic", + "hiragana", "katakana", "hiragana-iroha", "katakana-iroha", + "japanese-informal", "japanese-formal", "korean-hangul-formal", + "korean-hanja-informal", "korean-hanja-formal", + "simp-chinese-informal", "simp-chinese-formal", + "trad-chinese-informal", "trad-chinese-formal", + "ethiopic-numeric", + "-moz-cjk-heavenly-stem", "-moz-cjk-earthly-branch", + "-moz-trad-chinese-informal", "-moz-trad-chinese-formal", + "-moz-simp-chinese-informal", "-moz-simp-chinese-formal", + "-moz-japanese-informal", "-moz-japanese-formal", + "-moz-arabic-indic", "-moz-persian", "-moz-urdu", + "-moz-devanagari", "-moz-gurmukhi", "-moz-gujarati", + "-moz-oriya", "-moz-kannada", "-moz-malayalam", "-moz-bengali", + "-moz-tamil", "-moz-telugu", "-moz-thai", "-moz-lao", + "-moz-myanmar", "-moz-khmer", + "-moz-hangul", "-moz-hangul-consonant", + "-moz-ethiopic-halehame", "-moz-ethiopic-numeric", + "-moz-ethiopic-halehame-am", + "-moz-ethiopic-halehame-ti-er", "-moz-ethiopic-halehame-ti-et", + "other-style", "inside", "outside", "\\32 style", + '"-"', "'-'", + "symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")", + "symbols(cyclic '*' '\\2020' '\\2021' '\\A7')" + ], + invalid_values: [] + }, + "margin": { + domProp: "margin", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "margin-top", "margin-right", "margin-bottom", "margin-left" ], + initial_values: [ "0", "0px 0 0em", "0% 0px 0em 0pt" ], + other_values: [ "3px 0", "2em 4px 2pt", "1em 2em 3px 4px", "1em calc(2em + 3px) 4ex 5cm" ], + invalid_values: [ "1px calc(nonsense)", "1px red" ], + unbalanced_values: [ "1px calc(" ], + quirks_values: { "5": "5px", "3px 6px 2 5px": "3px 6px 2px 5px" }, + }, + "margin-bottom": { + domProp: "marginBottom", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "margin-left": { + domProp: "marginLeft", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", ".5px", "+32px", "+.789px", "-.328px", "+0.56px", "-0.974px", "237px", "-289px", "-056px", "1987.45px", "-84.32px", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ], + quirks_values: { "5": "5px" }, + }, + "margin-right": { + domProp: "marginRight", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "margin-top": { + domProp: "marginTop", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "max-height": { + domProp: "maxHeight", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block" }, + initial_values: [ "none", + // these four keywords compute to the initial value when the + // writing mode is horizontal, and that's the context we're testing in + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + ], + other_values: [ "30px", "50%", "0", + "calc(2px)", + "calc(-2px)", + "calc(0px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "auto" ], + quirks_values: { "5": "5px" }, + }, + "max-width": { + domProp: "maxWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block" }, + initial_values: [ "none" ], + other_values: [ "30px", "50%", "0", + // these four keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + "calc(2px)", + "calc(-2px)", + "calc(0px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "auto" ], + quirks_values: { "5": "5px" }, + }, + "min-height": { + domProp: "minHeight", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block" }, + initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)", + // these four keywords compute to the initial value when the + // writing mode is horizontal, and that's the context we're testing in + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + ], + other_values: [ "30px", "50%", + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: ["none"], + quirks_values: { "5": "5px" }, + }, + "min-width": { + domProp: "minWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block" }, + initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)" ], + other_values: [ "30px", "50%", + // these four keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none" ], + quirks_values: { "5": "5px" }, + }, + + "opacity": { + domProp: "opacity", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "17", "397.376", "3e1", "3e+1", "3e0", "3e+0", "3e-0" ], + other_values: [ "0", "0.4", "0.0000", "-3", "3e-1" ], + invalid_values: [ "0px", "1px" ] + }, + "-moz-orient": { + domProp: "MozOrient", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "inline" ], + other_values: [ "horizontal", "vertical", "block" ], + invalid_values: [ "none" ] + }, + "outline": { + domProp: "outline", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "outline-color", "outline-style", "outline-width" ], + initial_values: [ + "none", "medium", "thin", + // XXX Should be invert, but currently currentcolor. + //"invert", "none medium invert" + "currentColor", "none medium currentcolor" + ], + other_values: [ "solid", "medium solid", "green solid", "10px solid", "thick solid" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "outline-color": { + domProp: "outlineColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], // XXX should be invert + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000", "cc00ff" ] + }, + "outline-offset": { + domProp: "outlineOffset", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "-0", "calc(0px)", "calc(3em + 2px - 2px - 3em)", "calc(-0em)" ], + other_values: [ "-3px", "1em", "calc(3em)", "calc(7pt + 3 * 2em)", "calc(-3px)" ], + invalid_values: [ "5%" ] + }, + "outline-style": { + domProp: "outlineStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + // XXX Should 'hidden' be the same as initial? + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge", "auto" ], + invalid_values: [] + }, + "outline-width": { + domProp: "outlineWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "outline-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0px)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%", "5" ] + }, + "overflow": { + domProp: "overflow", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + prerequisites: { "display": "block", "contain": "none" }, + subproperties: [ "overflow-x", "overflow-y" ], + initial_values: [ "visible" ], + other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable", "-moz-scrollbars-none" ], + invalid_values: [] + }, + "overflow-x": { + domProp: "overflowX", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block", "overflow-y": "visible", "contain": "none" }, + initial_values: [ "visible" ], + other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ], + invalid_values: [] + }, + "overflow-y": { + domProp: "overflowY", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "display": "block", "overflow-x": "visible", "contain": "none" }, + initial_values: [ "visible" ], + other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ], + invalid_values: [] + }, + "padding": { + domProp: "padding", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "padding-top", "padding-right", "padding-bottom", "padding-left" ], + initial_values: [ "0", "0px 0 0em", "0% 0px 0em 0pt", "calc(0px) calc(0em) calc(-2px) calc(-1%)" ], + other_values: [ "3px 0", "2em 4px 2pt", "1em 2em 3px 4px" ], + invalid_values: [ "1px calc(nonsense)", "1px red" ], + unbalanced_values: [ "1px calc(" ], + quirks_values: { "5": "5px", "3px 6px 2 5px": "3px 6px 2px 5px" }, + }, + "padding-bottom": { + domProp: "paddingBottom", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "padding-left": { + domProp: "paddingLeft", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "padding-right": { + domProp: "paddingRight", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "padding-top": { + domProp: "paddingTop", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "page-break-after": { + domProp: "pageBreakAfter", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "always", "avoid", "left", "right" ], + invalid_values: [] + }, + "page-break-before": { + domProp: "pageBreakBefore", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "always", "avoid", "left", "right" ], + invalid_values: [] + }, + "page-break-inside": { + domProp: "pageBreakInside", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "avoid" ], + invalid_values: [ "left", "right" ] + }, + "pointer-events": { + domProp: "pointerEvents", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "visiblePainted", "visibleFill", "visibleStroke", "visible", + "painted", "fill", "stroke", "all", "none" ], + invalid_values: [] + }, + "position": { + domProp: "position", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "static" ], + other_values: [ "relative", "absolute", "fixed", "sticky" ], + invalid_values: [] + }, + "quotes": { + domProp: "quotes", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ '"\u201C" "\u201D" "\u2018" "\u2019"', + '"\\201C" "\\201D" "\\2018" "\\2019"' ], + other_values: [ "none", "'\"' '\"'" ], + invalid_values: [] + }, + "right": { + domProp: "right", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "ruby-align": { + domProp: "rubyAlign", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "space-around" ], + other_values: [ "start", "center", "space-between" ], + invalid_values: [ + "end", "1", "10px", "50%", "start center" + ] + }, + "ruby-position": { + domProp: "rubyPosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "over" ], + other_values: [ "under" ], + invalid_values: [ + "left", "right", "auto", "none", "not_a_position", + "over left", "right under", "0", "100px", "50%" + ] + }, + "table-layout": { + domProp: "tableLayout", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "fixed" ], + invalid_values: [] + }, + "text-align": { + domProp: "textAlign", + inherited: true, + type: CSS_TYPE_LONGHAND, + // don't know whether left and right are same as start + initial_values: [ "start" ], + other_values: [ "center", "justify", "end", "match-parent" ], + invalid_values: [ "true", "true true" ] + }, + "text-align-last": { + domProp: "textAlignLast", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "center", "justify", "start", "end", "left", "right" ], + invalid_values: [] + }, + "text-decoration": { + domProp: "textDecoration", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + subproperties: [ "text-decoration-color", "text-decoration-line", "text-decoration-style" ], + initial_values: [ "none" ], + other_values: [ "underline", "overline", "line-through", "blink", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration", + "underline red solid", "underline #ff0000", "solid underline", "red underline", "#ff0000 underline", "dotted underline" ], + invalid_values: [ "none none", "underline none", "none underline", "blink none", "none blink", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink", "rgb(0, rubbish, 0) underline" ] + }, + "text-decoration-color": { + domProp: "textDecorationColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000", "ff00ff" ] + }, + "text-decoration-line": { + domProp: "textDecorationLine", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "underline", "overline", "line-through", "blink", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration" ], + invalid_values: [ "none none", "underline none", "none underline", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink" ] + }, + "text-decoration-style": { + domProp: "textDecorationStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "solid" ], + other_values: [ "double", "dotted", "dashed", "wavy", "-moz-none" ], + invalid_values: [ "none", "groove", "ridge", "inset", "outset", "solid dashed", "wave" ] + }, + "text-emphasis": { + domProp: "textEmphasis", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "color": "black" }, + subproperties: [ "text-emphasis-style", "text-emphasis-color" ], + initial_values: [ "none currentColor", "currentColor none", "none", "currentColor", "none black" ], + other_values: [ "filled dot black", "#f00 circle open", "sesame filled rgba(0,0,255,0.5)", "red", "green none", "currentColor filled", "currentColor open" ], + invalid_values: [ "filled black dot", "filled filled red", "open open circle #000", "circle dot #f00", "rubbish" ] + }, + "text-emphasis-color": { + domProp: "textEmphasisColor", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor", "black", "rgb(0,0,0)" ], + other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] + }, + "text-emphasis-position": { + domProp: "textEmphasisPosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "over right", "right over" ], + other_values: [ "over left", "left over", "under left", "left under", "under right", "right under" ], + invalid_values: [ "over over", "left left", "over right left", "rubbish left", "over rubbish" ] + }, + "text-emphasis-style": { + domProp: "textEmphasisStyle", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "filled", "open", "dot", "circle", "double-circle", "triangle", "sesame", "'#'", + "filled dot", "filled circle", "filled double-circle", "filled triangle", "filled sesame", + "dot filled", "circle filled", "double-circle filled", "triangle filled", "sesame filled", + "dot open", "circle open", "double-circle open", "triangle open", "sesame open" ], + invalid_values: [ "rubbish", "dot rubbish", "rubbish dot", "open rubbish", "rubbish open", "open filled", "dot circle", + "open '#'", "'#' filled", "dot '#'", "'#' circle", "1", "1 open", "open 1" ] + }, + "text-indent": { + domProp: "textIndent", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "calc(3em - 5em + 2px + 2em - 2px)" ], + other_values: [ "2em", "5%", "-10px", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "text-overflow": { + domProp: "textOverflow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "clip" ], + other_values: [ "ellipsis", '""', "''", '"hello"', 'clip clip', 'ellipsis ellipsis', 'clip ellipsis', 'clip ""', '"hello" ""', '"" ellipsis' ], + invalid_values: [ "none", "auto", '"hello" inherit', 'inherit "hello"', 'clip initial', 'initial clip', 'initial inherit', 'inherit initial', 'inherit none'] + }, + "text-shadow": { + domProp: "textShadow", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "blue" }, + initial_values: [ "none" ], + other_values: [ "2px 2px", "2px 2px 1px", "2px 2px green", "2px 2px 1px green", "green 2px 2px", "green 2px 2px 1px", "green 2px 2px, blue 1px 3px 4px", "currentColor 3px 3px", "blue 2px 2px, currentColor 1px 2px", + /* calc() values */ + "2px 2px calc(-5px)", /* clamped */ + "calc(3em - 2px) 2px green", + "green calc(3em - 2px) 2px", + "2px calc(2px + 0.2em)", + "blue 2px calc(2px + 0.2em)", + "2px calc(2px + 0.2em) blue", + "calc(-2px) calc(-2px)", + "-2px -2px", + "calc(2px) calc(2px)", + "calc(2px) calc(2px) calc(2px)", + ], + invalid_values: [ "3% 3%", "2px 2px -5px", "2px 2px 2px 2px", "2px 2px, none", "none, 2px 2px", "inherit, 2px 2px", "2px 2px, inherit", "2 2px", "2px 2", "2px 2px 2", "2px 2px 2px 2", + "calc(2px) calc(2px) calc(2px) calc(2px)", "3px 3px calc(3px + rubbish)" + ] + }, + "text-transform": { + domProp: "textTransform", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "capitalize", "uppercase", "lowercase", "full-width" ], + invalid_values: [] + }, + "top": { + domProp: "top", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "transition": { + domProp: "transition", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ], + initial_values: [ "all 0s ease 0s", "all", "0s", "0s 0s", "ease" ], + other_values: [ "all 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0s", "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all", "3s --my-color" ], + invalid_values: [ "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial", "1s width,,2s color", "1s width, ,2s color", "bounce 1s cubic-bezier(0, rubbish) 2s", "bounce 1s steps(rubbish) 2s" ] + }, + "transition-delay": { + domProp: "transitionDelay", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0s", "0ms" ], + other_values: [ "1s", "250ms", "-100ms", "-1s", "1s, 250ms, 2.3s"], + invalid_values: [ "0", "0px" ] + }, + "transition-duration": { + domProp: "transitionDuration", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0s", "0ms" ], + other_values: [ "1s", "250ms", "1s, 250ms, 2.3s"], + invalid_values: [ "0", "0px", "-1ms", "-2s" ] + }, + "transition-property": { + domProp: "transitionProperty", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "all" ], + other_values: [ "none", "left", "top", "color", "width, height, opacity", "foobar", "auto", "\\32width", "-width", "-\\32width", "\\32 0width", "-\\32 0width", "\\2width", "-\\2width", "all, all", "all, color", "color, all", "--my-color" ], + invalid_values: [ "none, none", "color, none", "none, color", "inherit, color", "color, inherit", "initial, color", "color, initial", "none, color", "color, none" ] + }, + "transition-timing-function": { + domProp: "transitionTimingFunction", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "ease" ], + other_values: [ "cubic-bezier(0.25, 0.1, 0.25, 1.0)", "linear", "ease-in", "ease-out", "ease-in-out", "linear, ease-in, cubic-bezier(0.1, 0.2, 0.8, 0.9)", "cubic-bezier(0.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.25, 1.5, 0.75, -0.5)", "step-start", "step-end", "steps(1)", "steps(2, start)", "steps(386)", "steps(3, end)" ], + invalid_values: [ "none", "auto", "cubic-bezier(0.25, 0.1, 0.25)", "cubic-bezier(0.25, 0.1, 0.25, 0.25, 1.0)", "cubic-bezier(-0.5, 0.5, 0.5, 0.5)", "cubic-bezier(1.5, 0.5, 0.5, 0.5)", "cubic-bezier(0.5, 0.5, -0.5, 0.5)", "cubic-bezier(0.5, 0.5, 1.5, 0.5)", "steps(2, step-end)", "steps(0)", "steps(-2)", "steps(0, step-end, 1)" ] + }, + "unicode-bidi": { + domProp: "unicodeBidi", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "embed", "bidi-override", "isolate", "plaintext", "isolate-override", "-moz-isolate", "-moz-plaintext", "-moz-isolate-override" ], + invalid_values: [ "auto", "none" ] + }, + "vertical-align": { + domProp: "verticalAlign", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "baseline" ], + other_values: [ "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "-moz-middle-with-baseline", "15%", "3px", "0.2em", "-5px", "-3%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + quirks_values: { "5": "5px" }, + }, + "visibility": { + domProp: "visibility", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "visible" ], + other_values: [ "hidden", "collapse" ], + invalid_values: [] + }, + "white-space": { + domProp: "whiteSpace", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "pre", "nowrap", "pre-wrap", "pre-line", "-moz-pre-space" ], + invalid_values: [] + }, + "width": { + domProp: "width", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* computed value tests for width test more with display:block */ + prerequisites: { "display": "block" }, + initial_values: [ " auto" ], + /* XXX these have prerequisites */ + other_values: [ "15px", "3em", "15%", + // these three keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", + // whether -moz-available computes to the initial value depends on + // the container size, and for the container size we're testing + // with, it does + // "-moz-available", + "3e1px", "3e+1px", "3e0px", "3e+0px", "3e-0px", "3e-1px", + "3.2e1px", "3.2e+1px", "3.2e0px", "3.2e+0px", "3.2e-0px", "3.2e-1px", + "3e1%", "3e+1%", "3e0%", "3e+0%", "3e-0%", "3e-1%", + "3.2e1%", "3.2e+1%", "3.2e0%", "3.2e+0%", "3.2e-0%", "3.2e-1%", + /* valid -moz-calc() values */ + "-moz-calc(-2px)", + "-moz-calc(2px)", + "-moz-calc(50%)", + "-moz-calc(50% + 2px)", + "-moz-calc( 50% + 2px)", + "-moz-calc(50% + 2px )", + "-moz-calc( 50% + 2px )", + "-moz-calc(50% - -2px)", + "-moz-calc(2px - -50%)", + "-moz-calc(3*25px)", + "-moz-calc(3 *25px)", + "-moz-calc(3 * 25px)", + "-moz-calc(3* 25px)", + "-moz-calc(25px*3)", + "-moz-calc(25px *3)", + "-moz-calc(25px* 3)", + "-moz-calc(25px * 3)", + "-moz-calc(3*25px + 50%)", + "-moz-calc(50% - 3em + 2px)", + "-moz-calc(50% - (3em + 2px))", + "-moz-calc((50% - 3em) + 2px)", + "-moz-calc(2em)", + "-moz-calc(50%)", + "-moz-calc(50px/2)", + "-moz-calc(50px/(2 - 1))", + /* valid calc() values */ + "calc(-2px)", + "calc(2px)", + "calc(50%)", + "calc(50% + 2px)", + "calc( 50% + 2px)", + "calc(50% + 2px )", + "calc( 50% + 2px )", + "calc(50% - -2px)", + "calc(2px - -50%)", + "calc(3*25px)", + "calc(3 *25px)", + "calc(3 * 25px)", + "calc(3* 25px)", + "calc(25px*3)", + "calc(25px *3)", + "calc(25px* 3)", + "calc(25px * 3)", + "calc(3*25px + 50%)", + "calc(50% - 3em + 2px)", + "calc(50% - (3em + 2px))", + "calc((50% - 3em) + 2px)", + "calc(2em)", + "calc(50%)", + "calc(50px/2)", + "calc(50px/(2 - 1))", + ], + invalid_values: [ "none", "-2px", + /* invalid -moz-calc() values */ + "-moz-calc(50%+ 2px)", + "-moz-calc(50% +2px)", + "-moz-calc(50%+2px)", + /* invalid calc() values */ + "calc(50%+ 2px)", + "calc(50% +2px)", + "calc(50%+2px)", + "-moz-min()", + "calc(min())", + "-moz-max()", + "calc(max())", + "-moz-min(5px)", + "calc(min(5px))", + "-moz-max(5px)", + "calc(max(5px))", + "-moz-min(5px,2em)", + "calc(min(5px,2em))", + "-moz-max(5px,2em)", + "calc(max(5px,2em))", + "calc(50px/(2 - 2))", + /* If we ever support division by values, which is + * complicated for the reasons described in + * http://lists.w3.org/Archives/Public/www-style/2010Jan/0007.html + * , we should support all 4 of these as described in + * http://lists.w3.org/Archives/Public/www-style/2009Dec/0296.html + */ + "calc((3em / 100%) * 3em)", + "calc(3em / 100% * 3em)", + "calc(3em * (3em / 100%))", + "calc(3em * 3em / 100%)", + ], + quirks_values: { "5": "5px" }, + }, + "will-change": { + domProp: "willChange", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "scroll-position", "contents", "transform", "opacity", "scroll-position, transform", "transform, opacity", "contents, transform", "property-that-doesnt-exist-yet" ], + invalid_values: [ "none", "all", "default", "auto, scroll-position", "scroll-position, auto", "transform scroll-position", ",", "trailing,", "will-change", "transform, will-change" ] + }, + "word-break": { + domProp: "wordBreak", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "break-all", "keep-all" ], + invalid_values: [] + }, + "word-spacing": { + domProp: "wordSpacing", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal", "0", "0px", "-0em", + "calc(-0px)", "calc(0em)" + ], + other_values: [ "1em", "2px", "-3px", "0%", "50%", "-120%", + "calc(1em)", "calc(1em + 3px)", + "calc(15px / 2)", "calc(15px/2)", + "calc(-2em)", "calc(0% + 0px)", + "calc(-10%/2 - 1em)" + ], + invalid_values: [], + quirks_values: { "5": "5px" }, + }, + "overflow-wrap": { + domProp: "overflowWrap", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "break-word" ], + invalid_values: [] + }, + "hyphens": { + domProp: "hyphens", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "manual" ], + other_values: [ "none", "auto" ], + invalid_values: [] + }, + "z-index": { + domProp: "zIndex", + inherited: false, + type: CSS_TYPE_LONGHAND, + /* XXX requires position */ + initial_values: [ "auto" ], + other_values: [ "0", "3", "-7000", "12000" ], + invalid_values: [ "3.0", "17.5", "3e1" ] + } + , + "clip-path": { + domProp: "clipPath", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#mypath)", "url('404.svg#mypath')" ], + invalid_values: [] + }, + "clip-rule": { + domProp: "clipRule", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "nonzero" ], + other_values: [ "evenodd" ], + invalid_values: [] + }, + "color-interpolation": { + domProp: "colorInterpolation", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "sRGB" ], + other_values: [ "auto", "linearRGB" ], + invalid_values: [] + }, + "color-interpolation-filters": { + domProp: "colorInterpolationFilters", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "linearRGB" ], + other_values: [ "sRGB", "auto" ], + invalid_values: [] + }, + "dominant-baseline": { + domProp: "dominantBaseline", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "use-script", "no-change", "reset-size", "ideographic", "alphabetic", "hanging", "mathematical", "central", "middle", "text-after-edge", "text-before-edge" ], + invalid_values: [] + }, + "fill": { + domProp: "fill", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "blue" }, + initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ], + other_values: [ "green", "#fc3", "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "none", "currentColor", "context-fill", "context-stroke" ], + invalid_values: [ "000000", "ff00ff", "url('#myserver') rgb(0, rubbish, 0)" ] + }, + "fill-opacity": { + domProp: "fillOpacity", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ], + other_values: [ "0", "0.3", "-7.3" ], + invalid_values: [] + }, + "fill-rule": { + domProp: "fillRule", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "nonzero" ], + other_values: [ "evenodd" ], + invalid_values: [] + }, + "filter": { + domProp: "filter", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#myfilt)" ], + invalid_values: [ "url(#myfilt) none" ] + }, + "flood-color": { + domProp: "floodColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "blue" }, + initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ], + other_values: [ "green", "#fc3", "currentColor" ], + invalid_values: [ "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "000000", "ff00ff" ] + }, + "flood-opacity": { + domProp: "floodOpacity", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "2.8", "1.000" ], + other_values: [ "0", "0.3", "-7.3" ], + invalid_values: [] + }, + "image-rendering": { + domProp: "imageRendering", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "optimizeSpeed", "optimizeQuality", "-moz-crisp-edges" ], + invalid_values: [] + }, + "lighting-color": { + domProp: "lightingColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "blue" }, + initial_values: [ "white", "#fff", "#ffffff", "rgb(255,255,255)", "rgba(255,255,255,1.0)", "rgba(255,255,255,42.0)" ], + other_values: [ "green", "#fc3", "currentColor" ], + invalid_values: [ "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "000000", "ff00ff" ] + }, + "marker": { + domProp: "marker", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "marker-start", "marker-mid", "marker-end" ], + initial_values: [ "none" ], + other_values: [ "url(#mysym)" ], + invalid_values: [ "none none", "url(#mysym) url(#mysym)", "none url(#mysym)", "url(#mysym) none" ] + }, + "marker-end": { + domProp: "markerEnd", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#mysym)" ], + invalid_values: [] + }, + "marker-mid": { + domProp: "markerMid", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#mysym)" ], + invalid_values: [] + }, + "marker-start": { + domProp: "markerStart", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#mysym)" ], + invalid_values: [] + }, + "shape-rendering": { + domProp: "shapeRendering", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "optimizeSpeed", "crispEdges", "geometricPrecision" ], + invalid_values: [] + }, + "stop-color": { + domProp: "stopColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "blue" }, + initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ], + other_values: [ "green", "#fc3", "currentColor" ], + invalid_values: [ "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "000000", "ff00ff" ] + }, + "stop-opacity": { + domProp: "stopOpacity", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "2.8", "1.000" ], + other_values: [ "0", "0.3", "-7.3" ], + invalid_values: [] + }, + "stroke": { + domProp: "stroke", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)", "green", "#fc3", "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "currentColor", "context-fill", "context-stroke" ], + invalid_values: [ "000000", "ff00ff" ] + }, + "stroke-dasharray": { + domProp: "strokeDasharray", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none", "context-value" ], + other_values: [ "5px,3px,2px", "5px 3px 2px", " 5px ,3px\t, 2px ", "1px", "5%", "3em" ], + invalid_values: [ "-5px,3px,2px", "5px,3px,-2px" ] + }, + "stroke-dashoffset": { + domProp: "strokeDashoffset", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "-0px", "0em", "context-value" ], + other_values: [ "3px", "3%", "1em" ], + invalid_values: [] + }, + "stroke-linecap": { + domProp: "strokeLinecap", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "butt" ], + other_values: [ "round", "square" ], + invalid_values: [] + }, + "stroke-linejoin": { + domProp: "strokeLinejoin", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "miter" ], + other_values: [ "round", "bevel" ], + invalid_values: [] + }, + "stroke-miterlimit": { + domProp: "strokeMiterlimit", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "4" ], + other_values: [ "1", "7", "5000", "1.1" ], + invalid_values: [ "0.9", "0", "-1", "3px", "-0.3" ] + }, + "stroke-opacity": { + domProp: "strokeOpacity", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1", "2.8", "1.000", "context-fill-opacity", "context-stroke-opacity" ], + other_values: [ "0", "0.3", "-7.3" ], + invalid_values: [] + }, + "stroke-width": { + domProp: "strokeWidth", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1px", "context-value" ], + other_values: [ "0", "0px", "-0em", "17px", "0.2em" ], + invalid_values: [ "-0.1px", "-3px" ] + }, + "text-anchor": { + domProp: "textAnchor", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "start" ], + other_values: [ "middle", "end" ], + invalid_values: [] + }, + "text-rendering": { + domProp: "textRendering", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "optimizeSpeed", "optimizeLegibility", "geometricPrecision" ], + invalid_values: [] + }, + "vector-effect": { + domProp: "vectorEffect", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "non-scaling-stroke" ], + invalid_values: [] + }, + "-moz-window-dragging": { + domProp: "MozWindowDragging", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "default" ], + other_values: [ "drag", "no-drag" ], + invalid_values: [ "none" ] + }, + "align-content": { + domProp: "alignContent", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "start", "end", "flex-start", "flex-end", "center", "left", + "right", "space-between", "space-around", "space-evenly", + "first baseline", "last baseline", "baseline", "stretch", "start safe", + "unsafe end", "unsafe end stretch", "end safe space-evenly" ], + invalid_values: [ "none", "5", "self-end", "safe", "normal unsafe", "unsafe safe", + "safe baseline", "baseline unsafe", "baseline end", "end normal", + "safe end unsafe start", "safe end unsafe", "normal safe start", + "unsafe end start", "end start safe", "space-between unsafe", + "stretch safe", "auto", "first", "last" ] + }, + "align-items": { + domProp: "alignItems", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + // Can't test 'left'/'right' here since that computes to 'start' for blocks. + other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end", + "center", "stretch", "first baseline", "last baseline", "baseline", + "unsafe left", "start", "center unsafe", "safe right", "center safe" ], + invalid_values: [ "space-between", "abc", "5%", "legacy", "legacy end", + "end legacy", "unsafe", "unsafe baseline", "normal unsafe", + "safe left unsafe", "safe stretch", "end end", "auto" ] + }, + "align-self": { + domProp: "alignSelf", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "normal", "start", "flex-start", "flex-end", "center", "stretch", + "first baseline", "last baseline", "baseline", "right safe", + "unsafe center", "self-start", "self-end safe" ], + invalid_values: [ "space-between", "abc", "30px", "stretch safe", "safe" ] + }, + "justify-content": { + domProp: "justifyContent", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "start", "end", "flex-start", "flex-end", "center", "left", + "right", "space-between", "space-around", "space-evenly", + "first baseline", "last baseline", "baseline", "stretch", "start safe", + "unsafe end", "unsafe end stretch", "end safe space-evenly" ], + invalid_values: [ "30px", "5%", "self-end", "safe", "normal unsafe", "unsafe safe", + "safe baseline", "baseline unsafe", "baseline end", "normal end", + "safe end unsafe start", "safe end unsafe", "normal safe start", + "unsafe end start", "end start safe", "space-around unsafe", + "safe stretch", "auto", "first", "last" ] + }, + "justify-items": { + domProp: "justifyItems", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto", "normal" ], + other_values: [ "end", "flex-start", "flex-end", "self-start", "self-end", + "center", "left", "right", "first baseline", "last baseline", + "baseline", "stretch", "start", "legacy left", "right legacy", + "legacy center", "unsafe right", "left unsafe", "safe right", + "center safe" ], + invalid_values: [ "space-between", "abc", "30px", "legacy", "legacy start", + "end legacy", "legacy baseline", "legacy legacy", "unsafe", + "safe legacy left", "legacy left safe", "legacy safe left", + "safe left legacy", "legacy left legacy", "baseline unsafe", + "safe unsafe", "safe left unsafe", "safe stretch", "last" ] + }, + "justify-self": { + domProp: "justifySelf", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "normal", "start", "end", "flex-start", "flex-end", "self-start", + "self-end", "center", "left", "right", "baseline", "first baseline", + "last baseline", "stretch", "left unsafe", "unsafe right", + "safe right", "center safe" ], + invalid_values: [ "space-between", "abc", "30px", "none", "first", "last", + "legacy left", "right legacy", "baseline first", "baseline last" ] + }, + "place-content": { + domProp: "placeContent", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "align-content", "justify-content" ], + initial_values: [ "normal" ], + other_values: [ "normal start", "end baseline", "end end", + "space-between flex-end", "last baseline start", + "space-evenly", "flex-start", "end", "left" ], + invalid_values: [ "none", "center safe", "unsafe start", "right / end" ] + }, + "place-items": { + domProp: "placeItems", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "align-items", "justify-items" ], + initial_values: [ "normal" ], + other_values: [ "normal center", "end baseline", "end auto", + "end", "right", "baseline", "start last baseline", + "left flex-end", "last baseline start", "stretch" ], + invalid_values: [ "space-between", "start space-evenly", "none", "end/end", + "center safe", "auto start", "end legacy left" ] + }, + "place-self": { + domProp: "placeSelf", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "align-self", "justify-self" ], + initial_values: [ "auto" ], + other_values: [ "normal start", "end first baseline", "end auto", + "end", "right", "normal", "baseline", "start baseline", + "left self-end", "last baseline start", "stretch" ], + invalid_values: [ "space-between", "start space-evenly", "none", "end safe", + "auto legacy left", "legacy left", "auto/auto" ] + }, + "flex": { + domProp: "flex", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "flex-grow", + "flex-shrink", + "flex-basis" + ], + initial_values: [ "0 1 auto", "auto 0 1", "0 auto", "auto 0" ], + other_values: [ + "none", + "1", + "0", + "0 1", + "0.5", + "1.2 3.4", + "0 0 0", + "0 0 0px", + "0px 0 0", + "5px 0 0", + "2 auto", + "auto 4", + "auto 5.6 7.8", + "-moz-max-content", + "1 -moz-max-content", + "1 2 -moz-max-content", + "-moz-max-content 1", + "-moz-max-content 1 2", + "-0" + ], + invalid_values: [ + "1 2px 3", + "1 auto 3", + "1px 2 3px", + "1px 2 3 4px", + "-1", + "1 -1", + "0 1 calc(0px + rubbish)", + ] + }, + "flex-basis": { + domProp: "flexBasis", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ " auto" ], + // NOTE: This is cribbed directly from the "width" chunk, since this + // property takes the exact same values as width (albeit with + // different semantics on 'auto'). + // XXXdholbert (Maybe these should get separated out into + // a reusable array defined at the top of this file?) + other_values: [ "15px", "3em", "15%", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + // valid calc() values + "calc(-2px)", + "calc(2px)", + "calc(50%)", + "calc(50% + 2px)", + "calc( 50% + 2px)", + "calc(50% + 2px )", + "calc( 50% + 2px )", + "calc(50% - -2px)", + "calc(2px - -50%)", + "calc(3*25px)", + "calc(3 *25px)", + "calc(3 * 25px)", + "calc(3* 25px)", + "calc(25px*3)", + "calc(25px *3)", + "calc(25px* 3)", + "calc(25px * 3)", + "calc(3*25px + 50%)", + "calc(50% - 3em + 2px)", + "calc(50% - (3em + 2px))", + "calc((50% - 3em) + 2px)", + "calc(2em)", + "calc(50%)", + "calc(50px/2)", + "calc(50px/(2 - 1))" + ], + invalid_values: [ "none", "-2px", + // invalid calc() values + "calc(50%+ 2px)", + "calc(50% +2px)", + "calc(50%+2px)", + "-moz-min()", + "calc(min())", + "-moz-max()", + "calc(max())", + "-moz-min(5px)", + "calc(min(5px))", + "-moz-max(5px)", + "calc(max(5px))", + "-moz-min(5px,2em)", + "calc(min(5px,2em))", + "-moz-max(5px,2em)", + "calc(max(5px,2em))", + "calc(50px/(2 - 2))", + // If we ever support division by values, which is + // complicated for the reasons described in + // http://lists.w3.org/Archives/Public/www-style/2010Jan/0007.html + // , we should support all 4 of these as described in + // http://lists.w3.org/Archives/Public/www-style/2009Dec/0296.html + "calc((3em / 100%) * 3em)", + "calc(3em / 100% * 3em)", + "calc(3em * (3em / 100%))", + "calc(3em * 3em / 100%)" + ] + }, + "flex-direction": { + domProp: "flexDirection", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "row" ], + other_values: [ "row-reverse", "column", "column-reverse" ], + invalid_values: [ "10px", "30%", "justify", "column wrap" ] + }, + "flex-flow": { + domProp: "flexFlow", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "flex-direction", + "flex-wrap" + ], + initial_values: [ "row nowrap", "nowrap row", "row", "nowrap" ], + other_values: [ + // only specifying one property: + "column", + "wrap", + "wrap-reverse", + // specifying both properties, 'flex-direction' first: + "row wrap", + "row wrap-reverse", + "column wrap", + "column wrap-reverse", + // specifying both properties, 'flex-wrap' first: + "wrap row", + "wrap column", + "wrap-reverse row", + "wrap-reverse column", + ], + invalid_values: [ + // specifying flex-direction twice (invalid): + "row column", + "row column nowrap", + "row nowrap column", + "nowrap row column", + // specifying flex-wrap twice (invalid): + "nowrap wrap-reverse", + "nowrap wrap-reverse row", + "nowrap row wrap-reverse", + "row nowrap wrap-reverse", + // Invalid data-type / invalid keyword type: + "1px", "5%", "justify", "none" + ] + }, + "flex-grow": { + domProp: "flexGrow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0" ], + other_values: [ "3", "1", "1.0", "2.5", "123" ], + invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ] + }, + "flex-shrink": { + domProp: "flexShrink", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "1" ], + other_values: [ "3", "0", "0.0", "2.5", "123" ], + invalid_values: [ "0px", "-5", "1%", "3em", "stretch", "auto" ] + }, + "flex-wrap": { + domProp: "flexWrap", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "nowrap" ], + other_values: [ "wrap", "wrap-reverse" ], + invalid_values: [ "10px", "30%", "justify", "column wrap", "auto" ] + }, + "order": { + domProp: "order", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0" ], + other_values: [ "1", "99999", "-1", "-50" ], + invalid_values: [ "0px", "1.0", "1.", "1%", "0.2", "3em", "stretch" ] + }, + + // Aliases + "word-wrap": { + domProp: "wordWrap", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "overflow-wrap", + subproperties: [ "overflow-wrap" ], + }, + "-moz-transform": { + domProp: "MozTransform", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform", + subproperties: [ "transform" ], + // NOTE: We specify other_values & invalid_values explicitly here (instead + // of deferring to "transform") because we accept some legacy syntax as + // valid for "-moz-transform" but not for "transform". + other_values: [ "translatex(1px)", "translatex(4em)", + "translatex(-4px)", "translatex(3px)", + "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", + "translatey(4em)", "translate(3px)", "translate(10px, -3px)", + "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", + "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)", + "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", + "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)", + "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)", + "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", + "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", + "translatex(50%)", "translatey(50%)", "translate(50%)", + "translate(3%, 5px)", "translate(5px, 3%)", + "matrix(1, 2, 3, 4, 5, 6)", + /* valid calc() values */ + "translatex(calc(5px + 10%))", + "translatey(calc(0.25 * 5px + 10% / 3))", + "translate(calc(5px - 10% * 3))", + "translate(calc(5px - 3 * 10%), 50px)", + "translate(-50px, calc(5px - 10% * 3))", + /* valid only when prefixed */ + "matrix(1, 2, 3, 4, 5px, 6%)", + "matrix(1, 2, 3, 4, 5%, 6px)", + "matrix(1, 2, 3, 4, 5%, 6%)", + "matrix(1, 2, 3, 4, 5px, 6em)", + "matrix(1, 0, 0, 1, calc(5px * 3), calc(10% - 3px))", + "translatez(1px)", "translatez(4em)", "translatez(-4px)", + "translatez(0px)", "translatez(2px) translatez(5px)", + "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)", + "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)", + "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)", + "scalez(-6)", "rotate3d(2, 3, 4, 45deg)", + "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)", + "rotatez(72rad)", "rotatex(0.125turn)", + "perspective(0px)", "perspective(1000px)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)", + /* valid only when prefixed */ + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)", + ], + invalid_values: ["1px", "#0000ff", "red", "auto", + "translatex(1)", "translatey(1)", "translate(2)", + "translate(-3, -4)", + "translatex(1px 1px)", "translatex(translatex(1px))", + "translatex(#0000ff)", "translatex(red)", "translatey()", + "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)", + "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)", + "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)", + "matrix(0, 1, 2, 3%, 4%, 5%)", + /* invalid calc() values */ + "translatey(-moz-min(5px,10%))", + "translatex(-moz-max(5px,10%))", + "translate(10px, calc(min(5px,10%)))", + "translate(calc(max(5px,10%)), 10%)", + "matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))", + "perspective(-10px)", "matrix3d(dinosaur)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15%, 16)", + "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16px)", + "rotatey(words)", "rotatex(7)", "translate3d(3px, 4px, 1px, 7px)", + ], + }, + "-moz-transform-origin": { + domProp: "MozTransformOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform-origin", + subproperties: [ "transform-origin" ], + }, + "-moz-perspective-origin": { + domProp: "MozPerspectiveOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "perspective-origin", + subproperties: [ "perspective-origin" ], + }, + "-moz-perspective": { + domProp: "MozPerspective", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "perspective", + subproperties: [ "perspective" ], + }, + "-moz-backface-visibility": { + domProp: "MozBackfaceVisibility", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "backface-visibility", + subproperties: [ "backface-visibility" ], + }, + "-moz-transform-style": { + domProp: "MozTransformStyle", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform-style", + subproperties: [ "transform-style" ], + }, + "-moz-border-image": { + domProp: "MozBorderImage", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "border-image", + subproperties: [ "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ], + }, + "-moz-transition": { + domProp: "MozTransition", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "transition", + subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ], + }, + "-moz-transition-delay": { + domProp: "MozTransitionDelay", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-delay", + subproperties: [ "transition-delay" ], + }, + "-moz-transition-duration": { + domProp: "MozTransitionDuration", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-duration", + subproperties: [ "transition-duration" ], + }, + "-moz-transition-property": { + domProp: "MozTransitionProperty", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-property", + subproperties: [ "transition-property" ], + }, + "-moz-transition-timing-function": { + domProp: "MozTransitionTimingFunction", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-timing-function", + subproperties: [ "transition-timing-function" ], + }, + "-moz-animation": { + domProp: "MozAnimation", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "animation", + subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ], + }, + "-moz-animation-delay": { + domProp: "MozAnimationDelay", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-delay", + subproperties: [ "animation-delay" ], + }, + "-moz-animation-direction": { + domProp: "MozAnimationDirection", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-direction", + subproperties: [ "animation-direction" ], + }, + "-moz-animation-duration": { + domProp: "MozAnimationDuration", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-duration", + subproperties: [ "animation-duration" ], + }, + "-moz-animation-fill-mode": { + domProp: "MozAnimationFillMode", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-fill-mode", + subproperties: [ "animation-fill-mode" ], + }, + "-moz-animation-iteration-count": { + domProp: "MozAnimationIterationCount", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-iteration-count", + subproperties: [ "animation-iteration-count" ], + }, + "-moz-animation-name": { + domProp: "MozAnimationName", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-name", + subproperties: [ "animation-name" ], + }, + "-moz-animation-play-state": { + domProp: "MozAnimationPlayState", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-play-state", + subproperties: [ "animation-play-state" ], + }, + "-moz-animation-timing-function": { + domProp: "MozAnimationTimingFunction", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-timing-function", + subproperties: [ "animation-timing-function" ], + }, + "-moz-font-feature-settings": { + domProp: "MozFontFeatureSettings", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "font-feature-settings", + subproperties: [ "font-feature-settings" ], + }, + "-moz-font-language-override": { + domProp: "MozFontLanguageOverride", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "font-language-override", + subproperties: [ "font-language-override" ], + }, + "-moz-hyphens": { + domProp: "MozHyphens", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "hyphens", + subproperties: [ "hyphens" ], + }, + "-moz-text-align-last": { + domProp: "MozTextAlignLast", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "text-align-last", + subproperties: [ "text-align-last" ], + }, + // vertical text properties + "writing-mode": { + domProp: "writingMode", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "horizontal-tb", "lr", "lr-tb", "rl", "rl-tb" ], + other_values: [ "vertical-lr", "vertical-rl", "sideways-rl", "sideways-lr", "tb", "tb-rl" ], + invalid_values: [ "10px", "30%", "justify", "auto", "1em" ] + }, + "text-orientation": { + domProp: "textOrientation", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "mixed" ], + other_values: [ "upright", "sideways", "sideways-right" ], /* sideways-right alias for backward compatibility */ + invalid_values: [ "none", "3em", "sideways-left" ] /* sideways-left removed from CSS Writing Modes */ + }, + "border-block-end": { + domProp: "borderBlockEnd", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-block-end-color", "border-block-end-style", "border-block-end-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "block-size": { + domProp: "blockSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + /* XXX testing auto has prerequisites */ + initial_values: [ "auto" ], + prerequisites: { "display": "block" }, + other_values: [ "15px", "3em", "15%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ], + }, + "border-block-end-color": { + domProp: "borderBlockEndColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ] + }, + "border-block-end-style": { + domProp: "borderBlockEndStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-block-end-width": { + domProp: "borderBlockEndWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + prerequisites: { "border-block-end-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%", "5" ] + }, + "border-block-start": { + domProp: "borderBlockStart", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "border-block-start-color", "border-block-start-style", "border-block-start-width" ], + initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], + other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], + invalid_values: [ "5%", "5", "5 solid green" ] + }, + "border-block-start-color": { + domProp: "borderBlockStartColor", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "currentColor" ], + other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000" ] + }, + "border-block-start-style": { + domProp: "borderBlockStartStyle", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX hidden is sometimes the same as initial */ + initial_values: [ "none" ], + other_values: [ "solid", "dashed", "dotted", "double", "outset", "inset", "groove", "ridge" ], + invalid_values: [] + }, + "border-block-start-width": { + domProp: "borderBlockStartWidth", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + prerequisites: { "border-block-start-style": "solid" }, + initial_values: [ "medium", "3px", "calc(4px - 1px)" ], + other_values: [ "thin", "thick", "1px", "2em", + "calc(2px)", + "calc(-2px)", + "calc(0em)", + "calc(0px)", + "calc(5em)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 5em)", + ], + invalid_values: [ "5%", "5" ] + }, + "-moz-border-end": { + domProp: "MozBorderEnd", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "border-inline-end", + subproperties: [ "-moz-border-end-color", "-moz-border-end-style", "-moz-border-end-width" ], + }, + "-moz-border-end-color": { + domProp: "MozBorderEndColor", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-end-color", + subproperties: [ "border-inline-end-color" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-border-end-style": { + domProp: "MozBorderEndStyle", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-end-style", + subproperties: [ "border-inline-end-style" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-border-end-width": { + domProp: "MozBorderEndWidth", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-end-width", + subproperties: [ "border-inline-end-width" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-border-start": { + domProp: "MozBorderStart", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "border-inline-start", + subproperties: [ "-moz-border-start-color", "-moz-border-start-style", "-moz-border-start-width" ], + }, + "-moz-border-start-color": { + domProp: "MozBorderStartColor", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-start-color", + subproperties: [ "border-inline-start-color" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-border-start-style": { + domProp: "MozBorderStartStyle", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-start-style", + subproperties: [ "border-inline-start-style" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-border-start-width": { + domProp: "MozBorderStartWidth", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-inline-start-width", + subproperties: [ "border-inline-start-width" ], + get_computed: logical_box_prop_get_computed, + }, + "inline-size": { + domProp: "inlineSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + /* XXX testing auto has prerequisites */ + initial_values: [ "auto" ], + prerequisites: { "display": "block" }, + other_values: [ "15px", "3em", "15%", + // these three keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", + // whether -moz-available computes to the initial value depends on + // the container size, and for the container size we're testing + // with, it does + // "-moz-available", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none" ], + }, + "margin-block-end": { + domProp: "marginBlockEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ], + }, + "margin-block-start": { + domProp: "marginBlockStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* XXX testing auto has prerequisites */ + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "..25px", ".+5px", ".px", "-.px", "++5px", "-+4px", "+-3px", "--7px", "+-.6px", "-+.5px", "++.7px", "--.4px" ], + }, + "-moz-margin-end": { + domProp: "MozMarginEnd", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "margin-inline-end", + subproperties: [ "margin-inline-end" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-margin-start": { + domProp: "MozMarginStart", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "margin-inline-start", + subproperties: [ "margin-inline-start" ], + get_computed: logical_box_prop_get_computed, + }, + "max-block-size": { + domProp: "maxBlockSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + prerequisites: { "display": "block" }, + initial_values: [ "none" ], + other_values: [ "30px", "50%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "auto", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ] + }, + "max-inline-size": { + domProp: "maxInlineSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + prerequisites: { "display": "block" }, + initial_values: [ "none" ], + other_values: [ "30px", "50%", + // these four keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "auto", "5" ] + }, + "min-block-size": { + domProp: "minBlockSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + prerequisites: { "display": "block" }, + initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)" ], + other_values: [ "30px", "50%", + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none", "5", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ] + }, + "min-inline-size": { + domProp: "minInlineSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + axis: true, + get_computed: logical_axis_prop_get_computed, + prerequisites: { "display": "block" }, + initial_values: [ "auto", "0", "calc(0em)", "calc(-2px)" ], + other_values: [ "30px", "50%", + // these four keywords compute to the initial value only when the + // writing mode is vertical, and we're testing with a horizontal + // writing mode + "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available", + "calc(-1%)", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ "none", "5" ] + }, + "offset-block-end": { + domProp: "offsetBlockEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "offset-block-start": { + domProp: "offsetBlockStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "offset-inline-end": { + domProp: "offsetInlineEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "offset-inline-start": { + domProp: "offsetInlineStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "padding-block-end": { + domProp: "paddingBlockEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + }, + "padding-block-start": { + domProp: "paddingBlockStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + initial_values: [ "0", "0px", "0%", "calc(0pt)", "calc(0% + 0px)", "calc(-3px)", "calc(-1%)" ], + other_values: [ "1px", "2em", "5%", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [ ], + }, + "-moz-padding-end": { + domProp: "MozPaddingEnd", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "padding-inline-end", + subproperties: [ "padding-inline-end" ], + get_computed: logical_box_prop_get_computed, + }, + "-moz-padding-start": { + domProp: "MozPaddingStart", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "padding-inline-start", + subproperties: [ "padding-inline-start" ], + get_computed: logical_box_prop_get_computed, + }, +} // end of gCSSProperties + +function logical_axis_prop_get_computed(cs, property) +{ + // Use defaults for these two properties in case the vertical text + // pref (which they live behind) is turned off. + var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb"; + var orientation = writingMode.substring(0, writingMode.indexOf("-")); + + var mappings = { + "block-size": { horizontal: "height", + vertical: "width", + sideways: "width" }, + "inline-size": { horizontal: "width", + vertical: "height", + sideways: "height" }, + "max-block-size": { horizontal: "max-height", + vertical: "max-width", + sideways: "max-width" }, + "max-inline-size": { horizontal: "max-width", + vertical: "max-height", + sideways: "max-height" }, + "min-block-size": { horizontal: "min-height", + vertical: "min-width", + sideways: "min-width" }, + "min-inline-size": { horizontal: "min-width", + vertical: "min-height", + sideways: "min-height" }, + }; + + if (!mappings[property]) { + throw "unexpected property " + property; + } + + var prop = mappings[property][orientation]; + if (!prop) { + throw "unexpected writing mode " + writingMode; + } + + return cs.getPropertyValue(prop); +} + +function logical_box_prop_get_computed(cs, property) +{ + // http://dev.w3.org/csswg/css-writing-modes-3/#logical-to-physical + + // Use default for writing-mode in case the vertical text + // pref (which it lives behind) is turned off. + var writingMode = cs.getPropertyValue("writing-mode") || "horizontal-tb"; + + var direction = cs.getPropertyValue("direction"); + + // keys in blockMappings are writing-mode values + var blockMappings = { + "horizontal-tb": { "start": "top", "end": "bottom" }, + "vertical-rl": { "start": "right", "end": "left" }, + "vertical-lr": { "start": "left", "end": "right" }, + "sideways-rl": { "start": "right", "end": "left" }, + "sideways-lr": { "start": "left", "end": "right" }, + }; + + // keys in inlineMappings are regular expressions that match against + // a {writing-mode,direction} pair as a space-separated string + var inlineMappings = { + "horizontal-tb ltr": { "start": "left", "end": "right" }, + "horizontal-tb rtl": { "start": "right", "end": "left" }, + "vertical-.. ltr": { "start": "bottom", "end": "top" }, + "vertical-.. rtl": { "start": "top", "end": "bottom" }, + "vertical-.. ltr": { "start": "top", "end": "bottom" }, + "vertical-.. rtl": { "start": "bottom", "end": "top" }, + "sideways-lr ltr": { "start": "bottom", "end": "top" }, + "sideways-lr rtl": { "start": "top", "end": "bottom" }, + "sideways-rl ltr": { "start": "top", "end": "bottom" }, + "sideways-rl rtl": { "start": "bottom", "end": "top" }, + }; + + var blockMapping = blockMappings[writingMode]; + var inlineMapping; + + // test each regular expression in inlineMappings against the + // {writing-mode,direction} pair + var key = `${writingMode} ${direction}`; + for (var k in inlineMappings) { + if (new RegExp(k).test(key)) { + inlineMapping = inlineMappings[k]; + break; + } + } + + if (!blockMapping || !inlineMapping) { + throw "Unexpected writing mode property values"; + } + + function physicalize(aProperty, aMapping, aLogicalPrefix) { + for (var logicalSide in aMapping) { + var physicalSide = aMapping[logicalSide]; + logicalSide = aLogicalPrefix + logicalSide; + aProperty = aProperty.replace(logicalSide, physicalSide); + } + return aProperty; + } + + if (/^-moz-/.test(property)) { + property = physicalize(property.substring(5), inlineMapping, ""); + } else if (/^offset-(block|inline)-(start|end)/.test(property)) { + property = property.substring(7); // we want "top" not "offset-top", e.g. + property = physicalize(property, blockMapping, "block-"); + property = physicalize(property, inlineMapping, "inline-"); + } else if (/-(block|inline)-(start|end)/.test(property)) { + property = physicalize(property, blockMapping, "block-"); + property = physicalize(property, inlineMapping, "inline-"); + } else { + throw "Unexpected property"; + } + return cs.getPropertyValue(property); +} + +// Get the computed value for a property. For shorthands, return the +// computed values of all the subproperties, delimited by " ; ". +function get_computed_value(cs, property) +{ + var info = gCSSProperties[property]; + if (info.type == CSS_TYPE_TRUE_SHORTHAND || + (info.type == CSS_TYPE_SHORTHAND_AND_LONGHAND && + (property == "text-decoration" || property == "mask"))) { + var results = []; + for (var idx in info.subproperties) { + var subprop = info.subproperties[idx]; + results.push(get_computed_value(cs, subprop)); + } + return results.join(" ; "); + } + if (info.get_computed) + return info.get_computed(cs, property); + return cs.getPropertyValue(property); +} + +if (IsCSSPropertyPrefEnabled("layout.css.touch_action.enabled")) { + gCSSProperties["touch-action"] = { + domProp: "touchAction", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: ["auto"], + other_values: ["none", "pan-x", "pan-y", "pan-x pan-y", "pan-y pan-x", "manipulation"], + invalid_values: ["zoom", "pinch", "tap", "10px", "2", "auto pan-x", "pan-x auto", "none pan-x", "pan-x none", + "auto pan-y", "pan-y auto", "none pan-y", "pan-y none", "pan-x pan-x", "pan-y pan-y", + "pan-x pan-y none", "pan-x none pan-y", "none pan-x pan-y", "pan-y pan-x none", "pan-y none pan-x", "none pan-y pan-x", + "pan-x pan-y auto", "pan-x auto pan-y", "auto pan-x pan-y", "pan-y pan-x auto", "pan-y auto pan-x", "auto pan-y pan-x", + "pan-x pan-y zoom", "pan-x zoom pan-y", "zoom pan-x pan-y", "pan-y pan-x zoom", "pan-y zoom pan-x", "zoom pan-y pan-x", + "pan-x pan-y pan-x", "pan-x pan-x pan-y", "pan-y pan-x pan-x", "pan-y pan-x pan-y", "pan-y pan-y pan-x", "pan-x pan-y pan-y", + "manipulation none", "none manipulation", "manipulation auto", "auto manipulation", "manipulation zoom", "zoom manipulation", + "manipulation manipulation", "manipulation pan-x", "pan-x manipulation", "manipulation pan-y", "pan-y manipulation", + "manipulation pan-x pan-y", "pan-x manipulation pan-y", "pan-x pan-y manipulation", + "manipulation pan-y pan-x", "pan-y manipulation pan-x", "pan-y pan-x manipulation"] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright.enabled")) { + gCSSProperties["text-combine-upright"] = { + domProp: "textCombineUpright", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "all" ], + invalid_values: [ "auto", "all 2", "none all", "digits -3", "digits 0", + "digits 12", "none 3", "digits 3.1415", "digits3", "digits 1", + "digits 3 all", "digits foo", "digits all", "digits 3.0" ] + }; + if (IsCSSPropertyPrefEnabled("layout.css.text-combine-upright-digits.enabled")) { + gCSSProperties["text-combine-upright"].other_values.push( + "digits", "digits 2", "digits 3", "digits 4", "digits 3"); + } +} + +if (IsCSSPropertyPrefEnabled("svg.paint-order.enabled")) { + gCSSProperties["paint-order"] = { + domProp: "paintOrder", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "fill", "fill stroke", "fill stroke markers", "stroke markers fill" ], + invalid_values: [ "fill stroke markers fill", "fill normal" ] + }; +} + +if (IsCSSPropertyPrefEnabled("svg.transform-box.enabled")) { + gCSSProperties["transform-box"] = { + domProp: "transformBox", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "border-box" ], + other_values: [ "fill-box", "view-box" ], + invalid_values: [] + }; +} + +var basicShapeOtherValues = [ + "polygon(20px 20px)", + "polygon(20px 20%)", + "polygon(20% 20%)", + "polygon(20rem 20em)", + "polygon(20cm 20mm)", + "polygon(20px 20px, 30px 30px)", + "polygon(20px 20px, 30% 30%, 30px 30px)", + "polygon(nonzero, 20px 20px, 30% 30%, 30px 30px)", + "polygon(evenodd, 20px 20px, 30% 30%, 30px 30px)", + + "content-box", + "padding-box", + "border-box", + "margin-box", + + "polygon(0 0) content-box", + "border-box polygon(0 0)", + "padding-box polygon( 0 20px , 30px 20% ) ", + "polygon(evenodd, 20% 20em) content-box", + "polygon(evenodd, 20vh 20em) padding-box", + "polygon(evenodd, 20vh calc(20% + 20em)) border-box", + "polygon(evenodd, 20vh 20vw) margin-box", + + "circle()", + "circle(at center)", + "circle(at top left 20px)", + "circle(at bottom right)", + "circle(20%)", + "circle(300px)", + "circle(calc(20px + 30px))", + "circle(farthest-side)", + "circle(closest-side)", + "circle(closest-side at center)", + "circle(farthest-side at top)", + "circle(20px at top right)", + "circle(40% at 50% 100%)", + "circle(calc(20% + 20%) at right bottom)", + "circle() padding-box", + + "ellipse()", + "ellipse(at center)", + "ellipse(at top left 20px)", + "ellipse(at bottom right)", + "ellipse(20% 20%)", + "ellipse(300px 50%)", + "ellipse(calc(20px + 30px) 10%)", + "ellipse(farthest-side closest-side)", + "ellipse(closest-side farthest-side)", + "ellipse(farthest-side farthest-side)", + "ellipse(closest-side closest-side)", + "ellipse(closest-side closest-side at center)", + "ellipse(20% farthest-side at top)", + "ellipse(20px 50% at top right)", + "ellipse(closest-side 40% at 50% 100%)", + "ellipse(calc(20% + 20%) calc(20px + 20cm) at right bottom)", + + "inset(1px)", + "inset(20% -20px)", + "inset(20em 4rem calc(20% + 20px))", + "inset(20vh 20vw 20pt 3%)", + "inset(5px round 3px)", + "inset(1px 2px round 3px / 3px)", + "inset(1px 2px 3px round 3px 2em / 20%)", + "inset(1px 2px 3px 4px round 3px 2vw 20% / 20px 3em 2vh 20%)", +]; + +var basicShapeInvalidValues = [ + "url(#test) url(#tes2)", + "polygon (0 0)", + "polygon(20px, 40px)", + "border-box content-box", + "polygon(0 0) polygon(0 0)", + "polygon(nonzero 0 0)", + "polygon(evenodd 20px 20px)", + "polygon(20px 20px, evenodd)", + "polygon(20px 20px, nonzero)", + "polygon(0 0) conten-box content-box", + "content-box polygon(0 0) conten-box", + "padding-box polygon(0 0) conten-box", + "polygon(0 0) polygon(0 0) content-box", + "polygon(0 0) content-box polygon(0 0)", + "polygon(0 0), content-box", + "polygon(0 0), polygon(0 0)", + "content-box polygon(0 0) polygon(0 0)", + "content-box polygon(0 0) none", + "none content-box polygon(0 0)", + "inherit content-box polygon(0 0)", + "initial polygon(0 0)", + "polygon(0 0) farthest-side", + "farthest-corner polygon(0 0)", + "polygon(0 0) farthest-corner", + "polygon(0 0) conten-box", + "polygon(0 0) polygon(0 0) farthest-corner", + "polygon(0 0) polygon(0 0) polygon(0 0)", + "border-box polygon(0, 0)", + "border-box padding-box", + "margin-box farthest-side", + "nonsense() border-box", + "border-box nonsense()", + + "circle(at)", + "circle(at 20% 20% 30%)", + "circle(20px 2px at center)", + "circle(2at center)", + "circle(closest-corner)", + "circle(at center top closest-side)", + "circle(-20px)", + "circle(farthest-side closest-side)", + "circle(20% 20%)", + "circle(at farthest-side)", + "circle(calc(20px + rubbish))", + + "ellipse(at)", + "ellipse(at 20% 20% 30%)", + "ellipse(20px at center)", + "ellipse(-20px 20px)", + "ellipse(closest-corner farthest-corner)", + "ellipse(20px -20px)", + "ellipse(-20px -20px)", + "ellipse(farthest-side)", + "ellipse(20%)", + "ellipse(at farthest-side farthest-side)", + "ellipse(at top left calc(20px + rubbish))", + + "polygon(at)", + "polygon(at 20% 20% 30%)", + "polygon(20px at center)", + "polygon(2px 2at center)", + "polygon(closest-corner farthest-corner)", + "polygon(at center top closest-side closest-side)", + "polygon(40% at 50% 100%)", + "polygon(40% farthest-side 20px at 50% 100%)", + + "inset()", + "inset(round)", + "inset(round 3px)", + "inset(1px round 1px 2px 3px 4px 5px)", + "inset(1px 2px 3px 4px 5px)", + "inset(1px, round 3px)", + "inset(1px, 2px)", + "inset(1px 2px, 3px)", + "inset(1px at 3px)", + "inset(1px round 1px // 2px)", + "inset(1px round)", + "inset(1px calc(2px + rubbish))", + "inset(1px round 2px calc(3px + rubbish))", +]; + +var basicShapeUnbalancedValues = [ + "polygon(30% 30%", + "polygon(nonzero, 20% 20px", + "polygon(evenodd, 20px 20px", + + "circle(", + "circle(40% at 50% 100%", + "ellipse(", + "ellipse(40% at 50% 100%", + + "inset(1px", + "inset(1px 2px", + "inset(1px 2px 3px", + "inset(1px 2px 3px 4px", + "inset(1px 2px 3px 4px round 5px", + "inset(1px 2px 3px 4px round 5px / 6px", +]; + +if (IsCSSPropertyPrefEnabled("layout.css.clip-path-shapes.enabled")) { + gCSSProperties["clip-path"] = { + domProp: "clipPath", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + // SVG reference clip-path + "url(#my-clip-path)", + + "fill-box", + "stroke-box", + "view-box", + + "polygon(evenodd, 20pt 20cm) fill-box", + "polygon(evenodd, 20ex 20pc) stroke-box", + "polygon(evenodd, 20rem 20in) view-box", + ].concat(basicShapeOtherValues), + invalid_values: basicShapeInvalidValues, + unbalanced_values: basicShapeUnbalancedValues, + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.shape-outside.enabled")) { + gCSSProperties["shape-outside"] = { + domProp: "shapeOutside", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "url(#my-shape-outside)", + ].concat(basicShapeOtherValues), + invalid_values: basicShapeInvalidValues, + unbalanced_values: basicShapeUnbalancedValues, + }; +} + + +if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) { + gCSSProperties["filter"] = { + domProp: "filter", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + // SVG reference filters + "url(#my-filter)", + "url(#my-filter-1) url(#my-filter-2)", + + // Filter functions + "opacity(50%) saturate(1.0)", + "invert(50%) sepia(0.1) brightness(90%)", + + // Mixed SVG reference filters and filter functions + "grayscale(1) url(#my-filter-1)", + "url(#my-filter-1) brightness(50%) contrast(0.9)", + + // Bad URLs + "url('badscheme:badurl')", + "blur(3px) url('badscheme:badurl') grayscale(50%)", + + "blur(0)", + "blur(0px)", + "blur(0.5px)", + "blur(3px)", + "blur(100px)", + "blur(0.1em)", + "blur(calc(-1px))", // Parses and becomes blur(0px). + "blur(calc(0px))", + "blur(calc(5px))", + "blur(calc(2 * 5px))", + + "brightness(0)", + "brightness(50%)", + "brightness(1)", + "brightness(1.0)", + "brightness(2)", + "brightness(350%)", + "brightness(4.567)", + + "contrast(0)", + "contrast(50%)", + "contrast(1)", + "contrast(1.0)", + "contrast(2)", + "contrast(350%)", + "contrast(4.567)", + + "drop-shadow(2px 2px)", + "drop-shadow(2px 2px 1px)", + "drop-shadow(2px 2px green)", + "drop-shadow(2px 2px 1px green)", + "drop-shadow(green 2px 2px)", + "drop-shadow(green 2px 2px 1px)", + "drop-shadow(currentColor 3px 3px)", + "drop-shadow(2px 2px calc(-5px))", /* clamped */ + "drop-shadow(calc(3em - 2px) 2px green)", + "drop-shadow(green calc(3em - 2px) 2px)", + "drop-shadow(2px calc(2px + 0.2em))", + "drop-shadow(blue 2px calc(2px + 0.2em))", + "drop-shadow(2px calc(2px + 0.2em) blue)", + "drop-shadow(calc(-2px) calc(-2px))", + "drop-shadow(-2px -2px)", + "drop-shadow(calc(2px) calc(2px))", + "drop-shadow(calc(2px) calc(2px) calc(2px))", + + "grayscale(0)", + "grayscale(50%)", + "grayscale(1)", + "grayscale(1.0)", + "grayscale(2)", + "grayscale(350%)", + "grayscale(4.567)", + + "hue-rotate(0deg)", + "hue-rotate(90deg)", + "hue-rotate(540deg)", + "hue-rotate(-90deg)", + "hue-rotate(10grad)", + "hue-rotate(1.6rad)", + "hue-rotate(-1.6rad)", + "hue-rotate(0.5turn)", + "hue-rotate(-2turn)", + + "invert(0)", + "invert(50%)", + "invert(1)", + "invert(1.0)", + "invert(2)", + "invert(350%)", + "invert(4.567)", + + "opacity(0)", + "opacity(50%)", + "opacity(1)", + "opacity(1.0)", + "opacity(2)", + "opacity(350%)", + "opacity(4.567)", + + "saturate(0)", + "saturate(50%)", + "saturate(1)", + "saturate(1.0)", + "saturate(2)", + "saturate(350%)", + "saturate(4.567)", + + "sepia(0)", + "sepia(50%)", + "sepia(1)", + "sepia(1.0)", + "sepia(2)", + "sepia(350%)", + "sepia(4.567)", + ], + invalid_values: [ + // none + "none none", + "url(#my-filter) none", + "none url(#my-filter)", + "blur(2px) none url(#my-filter)", + + // Nested filters + "grayscale(invert(1.0))", + + // Comma delimited filters + "url(#my-filter),", + "invert(50%), url(#my-filter), brightness(90%)", + + // Test the following situations for each filter function: + // - Invalid number of arguments + // - Comma delimited arguments + // - Wrong argument type + // - Argument value out of range + "blur()", + "blur(3px 5px)", + "blur(3px,)", + "blur(3px, 5px)", + "blur(#my-filter)", + "blur(0.5)", + "blur(50%)", + "blur(calc(0))", // Unitless zero in calc is not a valid length. + "blur(calc(0.1))", + "blur(calc(10%))", + "blur(calc(20px - 5%))", + "blur(-3px)", + + "brightness()", + "brightness(0.5 0.5)", + "brightness(0.5,)", + "brightness(0.5, 0.5)", + "brightness(#my-filter)", + "brightness(10px)", + "brightness(-1)", + + "contrast()", + "contrast(0.5 0.5)", + "contrast(0.5,)", + "contrast(0.5, 0.5)", + "contrast(#my-filter)", + "contrast(10px)", + "contrast(-1)", + + "drop-shadow()", + "drop-shadow(3% 3%)", + "drop-shadow(2px 2px -5px)", + "drop-shadow(2px 2px 2px 2px)", + "drop-shadow(2px 2px, none)", + "drop-shadow(none, 2px 2px)", + "drop-shadow(inherit, 2px 2px)", + "drop-shadow(2px 2px, inherit)", + "drop-shadow(2 2px)", + "drop-shadow(2px 2)", + "drop-shadow(2px 2px 2)", + "drop-shadow(2px 2px 2px 2)", + "drop-shadow(calc(2px) calc(2px) calc(2px) calc(2px))", + "drop-shadow(green 2px 2px, blue 1px 3px 4px)", + "drop-shadow(blue 2px 2px, currentColor 1px 2px)", + + "grayscale()", + "grayscale(0.5 0.5)", + "grayscale(0.5,)", + "grayscale(0.5, 0.5)", + "grayscale(#my-filter)", + "grayscale(10px)", + "grayscale(-1)", + + "hue-rotate()", + "hue-rotate(0)", + "hue-rotate(0.5 0.5)", + "hue-rotate(0.5,)", + "hue-rotate(0.5, 0.5)", + "hue-rotate(#my-filter)", + "hue-rotate(10px)", + "hue-rotate(-1)", + "hue-rotate(45deg,)", + + "invert()", + "invert(0.5 0.5)", + "invert(0.5,)", + "invert(0.5, 0.5)", + "invert(#my-filter)", + "invert(10px)", + "invert(-1)", + + "opacity()", + "opacity(0.5 0.5)", + "opacity(0.5,)", + "opacity(0.5, 0.5)", + "opacity(#my-filter)", + "opacity(10px)", + "opacity(-1)", + + "saturate()", + "saturate(0.5 0.5)", + "saturate(0.5,)", + "saturate(0.5, 0.5)", + "saturate(#my-filter)", + "saturate(10px)", + "saturate(-1)", + + "sepia()", + "sepia(0.5 0.5)", + "sepia(0.5,)", + "sepia(0.5, 0.5)", + "sepia(#my-filter)", + "sepia(10px)", + "sepia(-1)", + ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) { + var isGridTemplateSubgridValueEnabled = + IsCSSPropertyPrefEnabled("layout.css.grid-template-subgrid-value.enabled"); + + gCSSProperties["display"].other_values.push("grid", "inline-grid"); + gCSSProperties["grid-auto-flow"] = { + domProp: "gridAutoFlow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "row" ], + other_values: [ + "column", + "column dense", + "row dense", + "dense column", + "dense row", + "dense", + ], + invalid_values: [ + "", + "auto", + "none", + "10px", + "column row", + "dense row dense", + ] + }; + + gCSSProperties["grid-auto-columns"] = { + domProp: "gridAutoColumns", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ + "40px", + "2em", + "2.5fr", + "12%", + "min-content", + "max-content", + "calc(2px - 99%)", + "minmax(20px, max-content)", + "minmax(min-content, auto)", + "minmax(auto, max-content)", + "m\\69nmax(20px, 4Fr)", + "MinMax(min-content, calc(20px + 10%))", + "fit-content(1px)", + "fit-content(calc(1px - 99%))", + "fit-content(10%)", + ], + invalid_values: [ + "", + "normal", + "40ms", + "-40px", + "-12%", + "-2em", + "-2.5fr", + "minmax()", + "minmax(20px)", + "mİnmax(20px, 100px)", + "minmax(20px, 100px, 200px)", + "maxmin(100px, 20px)", + "minmax(min-content, minmax(30px, max-content))", + "fit-content(-1px)", + "fit-content(auto)", + "fit-content(min-content)", + ] + }; + gCSSProperties["grid-auto-rows"] = { + domProp: "gridAutoRows", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: gCSSProperties["grid-auto-columns"].initial_values, + other_values: gCSSProperties["grid-auto-columns"].other_values, + invalid_values: gCSSProperties["grid-auto-columns"].invalid_values + }; + + gCSSProperties["grid-template-columns"] = { + domProp: "gridTemplateColumns", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "auto", + "40px", + "2.5fr", + "[normal] 40px [] auto [ ] 12%", + "[foo] 40px min-content [ bar ] calc(2px - 99%) max-content", + "40px min-content calc(20px + 10%) max-content", + "minmax(min-content, auto)", + "minmax(auto, max-content)", + "m\\69nmax(20px, 4Fr)", + "40px MinMax(min-content, calc(20px + 10%)) max-content", + "40px 2em", + "[] 40px [-foo] 2em [bar baz This\ is\ one\ ident]", + // TODO bug 978478: "[a] repeat(3, [b] 20px [c] 40px [d]) [e]", + "repeat(1, 20px)", + "repeat(1, [a] 20px)", + "[a] Repeat(4, [a] 20px [] auto [b c]) [d]", + "[a] 2.5fr Repeat(4, [a] 20px [] auto [b c]) [d]", + "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto [b c]) [d]", + "[a] 2.5fr [z] Repeat(4, [a] 20px [] auto) [d]", + "[a] 2.5fr [z] Repeat(4, 20px [b c] auto [b c]) [d]", + "[a] 2.5fr [z] Repeat(4, 20px auto) [d]", + "repeat(auto-fill, 0)", + "[a] repeat( Auto-fill,1%)", + "minmax(auto,0) [a] repeat(Auto-fit, 0) minmax(0,auto)", + "minmax(calc(1% + 1px),auto) repeat(Auto-fit,[] 1%) minmax(auto,1%)", + "[a] repeat( auto-fit,[a b] minmax(0,0) )", + "[a] 40px repeat(auto-fit,[a b] minmax(1px, 0) [])", + "[a] calc(1px - 99%) [b] repeat(auto-fit,[a b] minmax(1mm, 1%) [c]) [c]", + "repeat(auto-fill,minmax(1%,auto))", + "repeat(auto-fill,minmax(1em,min-content)) minmax(min-content,0)", + "repeat(auto-fill,minmax(max-content,1mm))", + "fit-content(1px) 1fr", + "[a] fit-content(calc(1px - 99%)) [b]", + "[a] fit-content(10%) [b c] fit-content(1em)", + ], + invalid_values: [ + "", + "normal", + "40ms", + "-40px", + "-12%", + "-2fr", + "[foo]", + "[inherit] 40px", + "[initial] 40px", + "[unset] 40px", + "[default] 40px", + "[span] 40px", + "[6%] 40px", + "[5th] 40px", + "[foo[] bar] 40px", + "[foo]] 40px", + "(foo) 40px", + "[foo] [bar] 40px", + "40px [foo] [bar]", + "minmax()", + "minmax(20px)", + "mİnmax(20px, 100px)", + "minmax(20px, 100px, 200px)", + "maxmin(100px, 20px)", + "minmax(min-content, minmax(30px, max-content))", + "repeat(0, 20px)", + "repeat(-3, 20px)", + "rêpeat(1, 20px)", + "repeat(1)", + "repeat(1, )", + "repeat(3px, 20px)", + "repeat(2.0, 20px)", + "repeat(2.5, 20px)", + "repeat(2, (foo))", + "repeat(2, foo)", + "40px calc(0px + rubbish)", + "repeat(1, repeat(1, 20px))", + "repeat(auto-fill, auto)", + "repeat(auto-fit,auto)", + "repeat(auto-fit,[])", + "repeat(auto-fill, 0) repeat(auto-fit, 0) ", + "repeat(auto-fit, 0) repeat(auto-fill, 0) ", + "[a] repeat(auto-fit, 0) repeat(auto-fit, 0) ", + "[a] repeat(auto-fill, 0) [a] repeat(auto-fill, 0) ", + "repeat(auto-fill, 0 0)", + "repeat(auto-fill, 0 [] 0)", + "repeat(auto-fill, min-content)", + "repeat(auto-fit,max-content)", + "repeat(auto-fit,1fr)", + "repeat(auto-fit,minmax(auto,auto))", + "repeat(auto-fit,minmax(min-content,1fr))", + "repeat(auto-fit,minmax(1fr,auto))", + "repeat(auto-fill,minmax(1fr,1em))", + "repeat(auto-fill, 10px) auto", + "auto repeat(auto-fit, 10px)", + "minmax(min-content,max-content) repeat(auto-fit, 0)", + "10px [a] 10px [b a] 1fr [b] repeat(auto-fill, 0)", + "fit-content(-1px)", + "fit-content(auto)", + "fit-content(min-content)", + ], + unbalanced_values: [ + "(foo] 40px", + ] + }; + if (isGridTemplateSubgridValueEnabled) { + gCSSProperties["grid-template-columns"].other_values.push( + // See https://bugzilla.mozilla.org/show_bug.cgi?id=981300 + "[none auto subgrid min-content max-content foo] 40px", + + "subgrid", + "subgrid [] [foo bar]", + "subgrid repeat(1, [])", + "subgrid Repeat(4, [a] [b c] [] [d])", + "subgrid repeat(auto-fill, [])", + "subgrid [x] repeat( Auto-fill, [a b c]) []", + "subgrid [x] repeat(auto-fill, []) [y z]" + ); + gCSSProperties["grid-template-columns"].invalid_values.push( + "subgrid [inherit]", + "subgrid [initial]", + "subgrid [unset]", + "subgrid [default]", + "subgrid [span]", + "subgrid [foo] 40px", + "subgrid [foo 40px]", + "[foo] subgrid", + "subgrid rêpeat(1, [])", + "subgrid repeat(0, [])", + "subgrid repeat(-3, [])", + "subgrid repeat(2.0, [])", + "subgrid repeat(2.5, [])", + "subgrid repeat(3px, [])", + "subgrid repeat(1)", + "subgrid repeat(1, )", + "subgrid repeat(2, [40px])", + "subgrid repeat(2, foo)", + "subgrid repeat(1, repeat(1, []))", + "subgrid repeat(auto-fit,[])", + "subgrid [] repeat(auto-fit,[])", + "subgrid [a] repeat(auto-fit,[])", + "subgrid repeat(auto-fill, 1px)", + "subgrid repeat(auto-fill, 1px [])", + "subgrid repeat(Auto-fill, [a] [b c] [] [d])", + "subgrid repeat(auto-fill, []) repeat(auto-fill, [])" + ); + } + gCSSProperties["grid-template-rows"] = { + domProp: "gridTemplateRows", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: gCSSProperties["grid-template-columns"].initial_values, + other_values: gCSSProperties["grid-template-columns"].other_values, + invalid_values: gCSSProperties["grid-template-columns"].invalid_values + }; + gCSSProperties["grid-template-areas"] = { + domProp: "gridTemplateAreas", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "''", + "'' ''", + "'1a-é_ .' \"b .\"", + "' Z\t\\aZ' 'Z Z'", + " '. . a b' '. .a b' ", + "'a.b' '. . .'", + "'.' '..'", + "'...' '.'", + "'...-blah' '. .'", + "'.. ..' '.. ...'", + ], + invalid_values: [ + "'a b' 'a/b'", + "'a . a'", + "'. a a' 'a a a'", + "'a a .' 'a a a'", + "'a a' 'a .'", + "'a a'\n'..'\n'a a'", + ] + }; + + gCSSProperties["grid-template"] = { + domProp: "gridTemplate", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "grid-template-areas", + "grid-template-rows", + "grid-template-columns", + ], + initial_values: [ + "none", + "none / none", + ], + other_values: [ + // <'grid-template-rows'> / <'grid-template-columns'> + "40px / 100px", + "[foo] 40px [bar] / [baz] repeat(auto-fill,100px) [fizz]", + " none/100px", + "40px/none", + // [ ? ? ? ]+ [ / ]? + "'fizz'", + "[bar] 'fizz'", + "'fizz' / [foo] 40px", + "[bar] 'fizz' / [foo] 40px", + "'fizz' 100px / [foo] 40px", + "[bar] 'fizz' 100px / [foo] 40px", + "[bar] 'fizz' 100px [buzz] / [foo] 40px", + "[bar] 'fizz' 100px [buzz] \n [a] '.' 200px [b] / [foo] 40px", + ], + invalid_values: [ + "'fizz' / repeat(1, 100px)", + "'fizz' repeat(1, 100px) / 0px", + "[foo] [bar] 40px / 100px", + "[fizz] [buzz] 100px / 40px", + "[fizz] [buzz] 'foo' / 40px", + "'foo' / none" + ] + }; + if (isGridTemplateSubgridValueEnabled) { + gCSSProperties["grid-template"].other_values.push( + "subgrid", + "subgrid/40px 20px", + "subgrid [foo] [] [bar baz] / 40px 20px", + "40px 20px/subgrid", + "40px 20px/subgrid [foo] [] repeat(3, [a] [b]) [bar baz]", + "subgrid/subgrid", + "subgrid [foo] [] [bar baz]/subgrid [foo] [] [bar baz]" + ); + gCSSProperties["grid-template"].invalid_values.push( + "subgrid []", + "subgrid [] / 'fizz'", + "subgrid / 'fizz'" + ); + } + + gCSSProperties["grid"] = { + domProp: "grid", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "grid-template-areas", + "grid-template-rows", + "grid-template-columns", + "grid-auto-flow", + "grid-auto-rows", + "grid-auto-columns", + "grid-column-gap", + "grid-row-gap", + ], + initial_values: [ + "none", + "none / none", + ], + other_values: [ + "auto-flow 40px / none", + "auto-flow / 40px", + "auto-flow dense auto / auto", + "dense auto-flow minmax(min-content, 2fr) / auto", + "dense auto-flow / 100px", + "none / auto-flow 40px", + "40px / auto-flow", + "none / dense auto-flow auto", + ].concat( + gCSSProperties["grid-template"].other_values + ), + invalid_values: [ + "auto-flow", + " / auto-flow", + "dense 0 / 0", + "dense dense 40px / 0", + "auto-flow / auto-flow", + "auto-flow / dense", + "auto-flow [a] 0 / 0", + "0 / auto-flow [a] 0", + "auto-flow -20px / 0", + "auto-flow 200ms / 0", + "auto-flow 40px 100px / 0", + ].concat( + gCSSProperties["grid-template"].invalid_values, + gCSSProperties["grid-auto-flow"].other_values, + gCSSProperties["grid-auto-flow"].invalid_values + .filter((v) => v != 'none') + ) + }; + + var gridLineOtherValues = [ + "foo", + "2", + "2 foo", + "foo 2", + "-3", + "-3 bar", + "bar -3", + "span 2", + "2 span", + "span foo", + "foo span", + "span 2 foo", + "span foo 2", + "2 foo span", + "foo 2 span", + ]; + var gridLineInvalidValues = [ + "", + "4th", + "span", + "inherit 2", + "2 inherit", + "20px", + "2 3", + "2.5", + "2.0", + "0", + "0 foo", + "span 0", + "2 foo 3", + "foo 2 foo", + "2 span foo", + "foo span 2", + "span -3", + "span -3 bar", + "span 2 span", + "span foo span", + "span 2 foo span", + ]; + + gCSSProperties["grid-column-start"] = { + domProp: "gridColumnStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: gridLineOtherValues, + invalid_values: gridLineInvalidValues + }; + gCSSProperties["grid-column-end"] = { + domProp: "gridColumnEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: gridLineOtherValues, + invalid_values: gridLineInvalidValues + }; + gCSSProperties["grid-row-start"] = { + domProp: "gridRowStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: gridLineOtherValues, + invalid_values: gridLineInvalidValues + }; + gCSSProperties["grid-row-end"] = { + domProp: "gridRowEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: gridLineOtherValues, + invalid_values: gridLineInvalidValues + }; + + // The grid-column and grid-row shorthands take values of the form + // [ / ]? + var gridColumnRowOtherValues = [].concat(gridLineOtherValues); + gridLineOtherValues.concat([ "auto" ]).forEach(function(val) { + gridColumnRowOtherValues.push(" foo / " + val); + gridColumnRowOtherValues.push(val + "/2"); + }); + var gridColumnRowInvalidValues = [ + "foo, bar", + "foo / bar / baz", + ].concat(gridLineInvalidValues); + gridLineInvalidValues.forEach(function(val) { + gridColumnRowInvalidValues.push("span 3 / " + val); + gridColumnRowInvalidValues.push(val + " / foo"); + }); + gCSSProperties["grid-column"] = { + domProp: "gridColumn", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "grid-column-start", + "grid-column-end" + ], + initial_values: [ "auto", "auto / auto" ], + other_values: gridColumnRowOtherValues, + invalid_values: gridColumnRowInvalidValues + }; + gCSSProperties["grid-row"] = { + domProp: "gridRow", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "grid-row-start", + "grid-row-end" + ], + initial_values: [ "auto", "auto / auto" ], + other_values: gridColumnRowOtherValues, + invalid_values: gridColumnRowInvalidValues + }; + + var gridAreaOtherValues = gridLineOtherValues.slice(); + gridLineOtherValues.forEach(function(val) { + gridAreaOtherValues.push("foo / " + val); + gridAreaOtherValues.push(val + "/2/3"); + gridAreaOtherValues.push("foo / bar / " + val + " / baz"); + }); + var gridAreaInvalidValues = [ + "foo, bar", + "foo / bar / baz / fizz / buzz", + "default / foo / bar / baz", + "foo / initial / bar / baz", + "foo / bar / inherit / baz", + "foo / bar / baz / unset", + ].concat(gridLineInvalidValues); + gridLineInvalidValues.forEach(function(val) { + gridAreaInvalidValues.push("foo / " + val); + gridAreaInvalidValues.push("foo / bar / " + val); + gridAreaInvalidValues.push("foo / 4 / bar / " + val); + }); + + gCSSProperties["grid-area"] = { + domProp: "gridArea", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "grid-row-start", + "grid-column-start", + "grid-row-end", + "grid-column-end" + ], + initial_values: [ + "auto", + "auto / auto", + "auto / auto / auto", + "auto / auto / auto / auto" + ], + other_values: gridAreaOtherValues, + invalid_values: gridAreaInvalidValues + }; + + gCSSProperties["grid-column-gap"] = { + domProp: "gridColumnGap", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0" ], + other_values: [ "2px", "2%", "1em", "calc(1px + 1em)", "calc(1%)", + "calc(1% + 1ch)" , "calc(1px - 99%)" ], + invalid_values: [ "-1px", "auto", "none", "1px 1px", "-1%", "fit-content(1px)" ], + }; + gCSSProperties["grid-row-gap"] = { + domProp: "gridRowGap", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0" ], + other_values: [ "2px", "2%", "1em", "calc(1px + 1em)", "calc(1%)", + "calc(1% + 1ch)" , "calc(1px - 99%)" ], + invalid_values: [ "-1px", "auto", "none", "1px 1px", "-1%", "min-content" ], + }; + gCSSProperties["grid-gap"] = { + domProp: "gridGap", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "grid-column-gap", "grid-row-gap" ], + initial_values: [ "0", "0 0" ], + other_values: [ "1ch 0", "1px 1%", "1em 1px", "calc(1px) calc(1%)" ], + invalid_values: [ "-1px", "1px -1px", "1px 1px 1px", "inherit 1px", + "1px auto" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.display-contents.enabled")) { + gCSSProperties["display"].other_values.push("contents"); +} + +if (IsCSSPropertyPrefEnabled("layout.css.contain.enabled")) { + gCSSProperties["contain"] = { + domProp: "contain", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "strict", + "layout", + "style", + "layout style", + "style layout", + "paint", + "layout paint", + "paint layout", + "style paint", + "paint style", + "layout style paint", + "layout paint style", + "style paint layout", + "paint style layout", + ], + invalid_values: [ + "none strict", + "strict layout", + "strict layout style", + "layout strict", + "layout style strict", + "layout style paint strict", + "paint strict", + "style strict", + "paint paint", + "strict strict", + "auto", + "10px", + "0", + ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.image-orientation.enabled")) { + gCSSProperties["image-orientation"] = { + domProp: "imageOrientation", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ + "0deg", + "0grad", + "0rad", + "0turn", + + // Rounded initial values. + "-90deg", + "15deg", + "360deg", + ], + other_values: [ + "0deg flip", + "90deg", + "90deg flip", + "180deg", + "180deg flip", + "270deg", + "270deg flip", + "flip", + "from-image", + + // Grad units. + "0grad flip", + "100grad", + "100grad flip", + "200grad", + "200grad flip", + "300grad", + "300grad flip", + + // Radian units. + "0rad flip", + "1.57079633rad", + "1.57079633rad flip", + "3.14159265rad", + "3.14159265rad flip", + "4.71238898rad", + "4.71238898rad flip", + + // Turn units. + "0turn flip", + "0.25turn", + "0.25turn flip", + "0.5turn", + "0.5turn flip", + "0.75turn", + "0.75turn flip", + + // Rounded values. + "-45deg flip", + "65deg flip", + "400deg flip", + ], + invalid_values: [ + "none", + "0deg none", + "flip 0deg", + "flip 0deg", + "0", + "0 flip", + "flip 0", + "0deg from-image", + "from-image 0deg", + "flip from-image", + "from-image flip", + ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.initial-letter.enabled")) { + gCSSProperties["initial-letter"] = { + domProp: "initialLetter", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "2", "2.5", "3.7 2", "4 3" ], + invalid_values: [ "-3", "3.7 -2", "25%", "16px", "1 0", "0", "0 1" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.osx-font-smoothing.enabled")) { + gCSSProperties["-moz-osx-font-smoothing"] = { + domProp: "MozOsxFontSmoothing", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "grayscale" ], + invalid_values: [ "none", "subpixel-antialiased", "antialiased" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.mix-blend-mode.enabled")) { + gCSSProperties["mix-blend-mode"] = { + domProp: "mixBlendMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: ["multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", + "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"], + invalid_values: [] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.isolation.enabled")) { + gCSSProperties["isolation"] = { + domProp: "isolation", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: ["isolate"], + invalid_values: [] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.background-blend-mode.enabled")) { + gCSSProperties["background-blend-mode"] = { + domProp: "backgroundBlendMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", + "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity" ], + invalid_values: ["none", "10px", "multiply multiply"] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.object-fit-and-position.enabled")) { + gCSSProperties["object-fit"] = { + domProp: "objectFit", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "fill" ], + other_values: [ "contain", "cover", "none", "scale-down" ], + invalid_values: [ "auto", "5px", "100%" ] + }; + gCSSProperties["object-position"] = { + domProp: "objectPosition", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "50% 50%", "50%", "center", "center center" ], + other_values: [ + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)", + "0px 0px", + "right 20px top 60px", + "right 20px bottom 60px", + "left 20px top 60px", + "left 20px bottom 60px", + "right -50px top -50px", + "left -50px bottom -50px", + "right 20px top -50px", + "right -20px top 50px", + "right 3em bottom 10px", + "bottom 3em right 10px", + "top 3em right 10px", + "left 15px", + "10px top", + "left top 15px", + "left 10px top", + "left 20%", + "right 20%" + ], + invalid_values: [ "center 10px center 4px", "center 10px center", + "top 20%", "bottom 20%", "50% left", "top 50%", + "50% bottom 10%", "right 10% 50%", "left right", + "top bottom", "left 10% right", + "top 20px bottom 20px", "left left", "20 20" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.overflow-clip-box.enabled")) { + gCSSProperties["overflow-clip-box"] = { + domProp: "overflowClipBox", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "padding-box" ], + other_values: [ "content-box" ], + invalid_values: [ "none", "auto", "border-box", "0" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.box-decoration-break.enabled")) { + gCSSProperties["box-decoration-break"] = { + domProp: "boxDecorationBreak", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "slice" ], + other_values: [ "clone" ], + invalid_values: [ "auto", "none", "1px" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.scroll-behavior.property-enabled")) { + gCSSProperties["scroll-behavior"] = { + domProp: "scrollBehavior", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "smooth" ], + invalid_values: [ "none", "1px" ] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.scroll-snap.enabled")) { + gCSSProperties["scroll-snap-coordinate"] = { + domProp: "scrollSnapCoordinate", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "25% 25%", "top", "0px 100px, 10em 50%", + "top left, top right, bottom left, bottom right, center", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + "calc(20%) calc(3*25px), center"], + invalid_values: [ "auto", "default" ] + } + gCSSProperties["scroll-snap-destination"] = { + domProp: "scrollSnapDestination", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0px 0px" ], + other_values: [ "25% 25%", "6px 5px", "20% 3em", "0in 1in", + "top", "right", "top left", "top right", "center", + "calc(2px)", + "calc(50%)", + "calc(3*25px)", + "calc(3*25px) 5px", + "5px calc(3*25px)", + "calc(20%) calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)"], + invalid_values: [ "auto", "none", "default" ] + } + gCSSProperties["scroll-snap-points-x"] = { + domProp: "scrollSnapPointsX", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ], + invalid_values: [ "auto", "1px", "left", "rgb(1,2,3)" ] + } + gCSSProperties["scroll-snap-points-y"] = { + domProp: "scrollSnapPointsY", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "repeat(100%)", "repeat(120px)", "repeat(calc(3*25px))" ], + invalid_values: [ "auto", "1px", "top", "rgb(1,2,3)" ] + } + gCSSProperties["scroll-snap-type"] = { + domProp: "scrollSnapType", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ "scroll-snap-type-x", "scroll-snap-type-y" ], + initial_values: [ "none" ], + other_values: [ "mandatory", "proximity" ], + invalid_values: [ "auto", "1px" ] + }; + gCSSProperties["scroll-snap-type-x"] = { + domProp: "scrollSnapTypeX", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: ["mandatory", "proximity"], + invalid_values: [ "auto", "1px" ] + }; + gCSSProperties["scroll-snap-type-y"] = { + domProp: "scrollSnapTypeY", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: ["mandatory", "proximity"], + invalid_values: [ "auto", "1px" ] + }; +} + +function SupportsMaskShorthand() { + return "maskImage" in document.documentElement.style; +} + +if (SupportsMaskShorthand()) { + gCSSProperties["mask"] = { + domProp: "mask", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + /* FIXME: All mask-border-* should be added when we implement them. */ + subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position-x", "mask-position-y", "mask-repeat", "mask-size" , "mask-composite"], + initial_values: [ "match-source", "none", "repeat", "add", "0% 0%", "top left", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto", + "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "top left / auto none", "left top / auto none", + "top left / auto auto none", + "match-source none repeat add top left", "top left repeat none add", "none repeat add top left / auto", "top left / auto repeat none add match-source", "none repeat add 0% 0% / auto auto match-source", + "border-box", "border-box border-box" ], + other_values: [ + "none alpha repeat add left top", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + "no-repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') alpha left top add", + "repeat-x", + "repeat-y", + "no-repeat", + "none repeat-y alpha add 0% 0%", + "subtract", + "0% top subtract alpha repeat none", + "top", + "left", + "50% 50%", + "center", + "top / 100px", + "left / contain", + "left / cover", + "10px / 10%", + "10em / calc(20px)", + "top left / 100px 100px", + "top left / 100px auto", + "top left / 100px 10%", + "top left / 100px calc(20px)", + "bottom right add none alpha repeat", + "50% alpha", + "alpha 50%", + "50%", + "url(#mymask)", + "-moz-radial-gradient(10% bottom, #ffffff, black) add no-repeat", + "-moz-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10px -0.125turn, red, blue) repeat", + "-moz-repeating-radial-gradient(10% bottom, #ffffff, black) add no-repeat", + "-moz-repeating-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-element(#test) alpha", + /* multiple mask-image */ + "url(404.png), url(404.png)", + "repeat-x, subtract, none", + "0% top url(404.png), url(404.png) 50% top", + "subtract repeat-y top left url(404.png), repeat-x alpha", + "url(404.png), -moz-linear-gradient(20px 20px -45deg, blue, green), -moz-element(#a) alpha", + "top left / contain, bottom right / cover", + /* test cases with clip+origin in the shorthand */ + "url(404.png) alpha padding-box", + "url(404.png) border-box alpha", + "content-box url(404.png)", + "url(404.png) alpha padding-box padding-box", + "url(404.png) alpha padding-box border-box", + "content-box border-box url(404.png)", + ], + invalid_values: [ + /* mixes with keywords have to be in correct order */ + "50% left", "top 50%", + /* no quirks mode colors */ + "-moz-radial-gradient(10% bottom, ffffff, black) add no-repeat", + /* no quirks mode lengths */ + "-moz-linear-gradient(10 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat", + "linear-gradient(red -99, yellow, green, blue 120%)", + /* bug 258080: don't accept background-position separated */ + "left url(404.png) top", "top url(404.png) left", + "alpha padding-box url(404.png) border-box", + "alpha padding-box url(404.png) padding-box", + "-moz-element(#a rubbish)", + "left top / match-source" + ] + }; + gCSSProperties["mask-clip"] = { + domProp: "maskClip", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "border-box" ], + other_values: [ "content-box", "padding-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "content-box content-box" ] + }; + gCSSProperties["mask-image"] = { + domProp: "maskImage", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==')", 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', + "none, none", + "none, none, none, none, none", + "url(#mymask)", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "none, url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), none", + "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", + ].concat(validGradientAndElementValues), + invalid_values: [ + ].concat(invalidGradientAndElementValues), + unbalanced_values: [ + ].concat(unbalancedGradientAndElementValues) + }; + gCSSProperties["mask-mode"] = { + domProp: "maskMode", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "match-source" ], + other_values: [ "alpha", "luminance", "match-source, match-source", "match-source, alpha", "alpha, luminance, match-source"], + invalid_values: [ "match-source match-source", "alpha match-source" ] + }; + gCSSProperties["mask-composite"] = { + domProp: "maskComposite", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "add" ], + other_values: [ "subtract", "intersect", "exclude", "add, add", "subtract, intersect", "subtract, subtract, add"], + invalid_values: [ "add subtract", "intersect exclude" ] + }; + gCSSProperties["mask-origin"] = { + domProp: "maskOrigin", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "border-box" ], + other_values: [ "padding-box", "content-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ], + invalid_values: [ "margin-box", "padding-box padding-box" ] + }; + gCSSProperties["mask-position"] = { + domProp: "maskPosition", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + initial_values: [ "top 0% left 0%", "top 0% left", "top left", "left top", "0% 0%", "0% top", "left 0%" ], + other_values: [ "top", "left", "right", "bottom", "center", "center bottom", "bottom center", "center right", "right center", "center top", "top center", "center left", "left center", "right bottom", "bottom right", "50%", "top left, top left", "top left, top right", "top right, top left", "left top, 0% 0%", "10% 20%, 30%, 40%", "top left, bottom right", "right bottom, left top", "0%", "0px", "30px", "0%, 10%, 20%, 30%", "top, top, top, top, top", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)", + "0px 0px", + "right 20px top 60px", + "right 20px bottom 60px", + "left 20px top 60px", + "left 20px bottom 60px", + "right -50px top -50px", + "left -50px bottom -50px", + "right 20px top -50px", + "right -20px top 50px", + "right 3em bottom 10px", + "bottom 3em right 10px", + "top 3em right 10px", + "left 15px", + "10px top", + "left top 15px", + "left 10px top", + "left 20%", + "right 20%" + ], + subproperties: [ "mask-position-x", "mask-position-y" ], + invalid_values: [ "center 10px center 4px", "center 10px center", + "top 20%", "bottom 20%", "50% left", "top 50%", + "50% bottom 10%", "right 10% 50%", "left right", + "top bottom", "left 10% right", + "top 20px bottom 20px", "left left", + "0px calc(0px + rubbish)"], + }; + gCSSProperties["mask-position-x"] = { + domProp: "maskPositionX", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "left", "0%" ], + other_values: [ "right", "center", "50%", "center, center", "center, right", "right, center", "center, 50%", "10%, 20%, 40%", "1px", "30px", "50%, 10%, 20%, 30%", "center, center, center, center, center", + "calc(20px)", + "calc(20px + 1em)", + "calc(20px / 2)", + "calc(20px + 50%)", + "calc(50% - 10px)", + "calc(-20px)", + "calc(-50%)", + "calc(-20%)", + "right 20px", + "left 20px", + "right -50px", + "left -50px", + "right 20px", + "right 3em", + ], + invalid_values: [ "center 10px", "right 10% 50%", "left right", "left left", + "bottom 20px", "top 10%", "bottom 3em", + "top", "bottom", "top, top", "top, bottom", "bottom, top", "top, 0%", "top, top, top, top, top", + "calc(0px + rubbish)", "center 0%"], + }; + gCSSProperties["mask-position-y"] = { + domProp: "maskPositionY", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "top", "0%" ], + other_values: [ "bottom", "center", "50%", "center, center", "center, bottom", "bottom, center", "center, 0%", "10%, 20%, 40%", "1px", "30px", "50%, 10%, 20%, 30%", "center, center, center, center, center", + "calc(20px)", + "calc(20px + 1em)", + "calc(20px / 2)", + "calc(20px + 50%)", + "calc(50% - 10px)", + "calc(-20px)", + "calc(-50%)", + "calc(-20%)", + "bottom 20px", + "top 20px", + "bottom -50px", + "top -50px", + "bottom 20px", + "bottom 3em", + ], + invalid_values: [ "center 10px", "bottom 10% 50%", "top bottom", "top top", + "right 20px", "left 10%", "right 3em", + "left", "right", "left, left", "left, right", "right, left", "left, 0%", "left, left, left, left, left", + "calc(0px + rubbish)", "center 0%"], + }; + gCSSProperties["mask-repeat"] = { + domProp: "maskRepeat", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "repeat", "repeat repeat" ], + other_values: [ "repeat-x", "repeat-y", "no-repeat", + "repeat-x, repeat-x", + "repeat, no-repeat", + "repeat-y, no-repeat, repeat-y", + "repeat, repeat, repeat", + "repeat no-repeat", + "no-repeat repeat", + "no-repeat no-repeat", + "repeat no-repeat", + "no-repeat no-repeat, no-repeat no-repeat", + ], + invalid_values: [ "repeat repeat repeat", + "repeat-x repeat-y", + "repeat repeat-x", + "repeat repeat-y", + "repeat-x repeat", + "repeat-y repeat" ] + }; + gCSSProperties["mask-size"] = { + domProp: "maskSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto", "auto auto" ], + other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%", + "calc(20px)", + "calc(20px) 10px", + "10px calc(20px)", + "calc(20px) 25%", + "25% calc(20px)", + "calc(20px) calc(20px)", + "calc(20px + 1em) calc(20px / 2)", + "calc(20px + 50%) calc(50% - 10px)", + "calc(-20px) calc(-50%)", + "calc(-20%) calc(-50%)" + ], + invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ] + }; +} else { + gCSSProperties["mask"] = { + domProp: "mask", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ "url(#mymask)" ], + invalid_values: [] + }; +} + +if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { + gCSSProperties["-webkit-animation"] = { + domProp: "webkitAnimation", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "animation", + subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ], + }; + gCSSProperties["-webkit-animation-delay"] = { + domProp: "webkitAnimationDelay", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-delay", + subproperties: [ "animation-delay" ], + }; + gCSSProperties["-webkit-animation-direction"] = { + domProp: "webkitAnimationDirection", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-direction", + subproperties: [ "animation-direction" ], + }; + gCSSProperties["-webkit-animation-duration"] = { + domProp: "webkitAnimationDuration", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-duration", + subproperties: [ "animation-duration" ], + }; + gCSSProperties["-webkit-animation-fill-mode"] = { + domProp: "webkitAnimationFillMode", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-fill-mode", + subproperties: [ "animation-fill-mode" ], + }; + gCSSProperties["-webkit-animation-iteration-count"] = { + domProp: "webkitAnimationIterationCount", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-iteration-count", + subproperties: [ "animation-iteration-count" ], + }; + gCSSProperties["-webkit-animation-name"] = { + domProp: "webkitAnimationName", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-name", + subproperties: [ "animation-name" ], + }; + gCSSProperties["-webkit-animation-play-state"] = { + domProp: "webkitAnimationPlayState", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-play-state", + subproperties: [ "animation-play-state" ], + }; + gCSSProperties["-webkit-animation-timing-function"] = { + domProp: "webkitAnimationTimingFunction", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "animation-timing-function", + subproperties: [ "animation-timing-function" ], + }; + gCSSProperties["-webkit-filter"] = { + domProp: "webkitFilter", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "filter", + subproperties: [ "filter" ], + }; + gCSSProperties["-webkit-text-fill-color"] = { + domProp: "webkitTextFillColor", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor", "black", "#000", "#000000", "rgb(0,0,0)" ], + other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] + }; + gCSSProperties["-webkit-text-stroke"] = { + domProp: "webkitTextStroke", + inherited: true, + type: CSS_TYPE_TRUE_SHORTHAND, + prerequisites: { "color": "black" }, + subproperties: [ "-webkit-text-stroke-width", "-webkit-text-stroke-color" ], + initial_values: [ "0 currentColor", "currentColor 0px", "0", "currentColor", "0px black" ], + other_values: [ "thin black", "#f00 medium", "thick rgba(0,0,255,0.5)", "calc(4px - 8px) green", "2px", "green 0", "currentColor 4em", "currentColor calc(5px - 1px)" ], + invalid_values: [ "-3px black", "calc(50%+ 2px) #000", "30% #f00" ] + }; + gCSSProperties["-webkit-text-stroke-color"] = { + domProp: "webkitTextStrokeColor", + inherited: true, + type: CSS_TYPE_LONGHAND, + prerequisites: { "color": "black" }, + initial_values: [ "currentColor", "black", "#000", "#000000", "rgb(0,0,0)" ], + other_values: [ "red", "rgba(255,255,255,0.5)", "transparent" ], + invalid_values: [ "#0", "#00", "#00000", "#0000000", "#000000000", "000000", "ff00ff", "rgb(255,xxx,255)" ] + }; + gCSSProperties["-webkit-text-stroke-width"] = { + domProp: "webkitTextStrokeWidth", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "0", "0px", "0em", "0ex", "calc(0pt)", "calc(4px - 8px)" ], + other_values: [ "thin", "medium", "thick", "17px", "0.2em", "calc(3*25px + 5em)", "calc(5px - 1px)" ], + invalid_values: [ "5%", "1px calc(nonsense)", "1px red", "-0.1px", "-3px", "30%" ] + }, + gCSSProperties["-webkit-text-size-adjust"] = { + domProp: "webkitTextSizeAdjust", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-text-size-adjust", + subproperties: [ "-moz-text-size-adjust" ], + }; + gCSSProperties["-webkit-transform"] = { + domProp: "webkitTransform", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform", + subproperties: [ "transform" ], + }; + gCSSProperties["-webkit-transform-origin"] = { + domProp: "webkitTransformOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform-origin", + subproperties: [ "transform-origin" ], + }; + gCSSProperties["-webkit-transform-style"] = { + domProp: "webkitTransformStyle", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transform-style", + subproperties: [ "transform-style" ], + }; + gCSSProperties["-webkit-backface-visibility"] = { + domProp: "webkitBackfaceVisibility", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "backface-visibility", + subproperties: [ "backface-visibility" ], + }; + gCSSProperties["-webkit-perspective"] = { + domProp: "webkitPerspective", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "perspective", + subproperties: [ "perspective" ], + }; + gCSSProperties["-webkit-perspective-origin"] = { + domProp: "webkitPerspectiveOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "perspective-origin", + subproperties: [ "perspective-origin" ], + }; + gCSSProperties["-webkit-transition"] = { + domProp: "webkitTransition", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "transition", + subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ], + }; + gCSSProperties["-webkit-transition-delay"] = { + domProp: "webkitTransitionDelay", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-delay", + subproperties: [ "transition-delay" ], + }; + gCSSProperties["-webkit-transition-duration"] = { + domProp: "webkitTransitionDuration", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-duration", + subproperties: [ "transition-duration" ], + }; + gCSSProperties["-webkit-transition-property"] = { + domProp: "webkitTransitionProperty", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-property", + subproperties: [ "transition-property" ], + }; + gCSSProperties["-webkit-transition-timing-function"] = { + domProp: "webkitTransitionTimingFunction", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "transition-timing-function", + subproperties: [ "transition-timing-function" ], + }; + gCSSProperties["-webkit-border-radius"] = { + domProp: "webkitBorderRadius", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "border-radius", + subproperties: [ "border-bottom-left-radius", "border-bottom-right-radius", "border-top-left-radius", "border-top-right-radius" ], + }; + gCSSProperties["-webkit-border-top-left-radius"] = { + domProp: "webkitBorderTopLeftRadius", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-top-left-radius", + subproperties: [ "border-top-left-radius" ], + }; + gCSSProperties["-webkit-border-top-right-radius"] = { + domProp: "webkitBorderTopRightRadius", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-top-right-radius", + subproperties: [ "border-top-right-radius" ], + }; + gCSSProperties["-webkit-border-bottom-left-radius"] = { + domProp: "webkitBorderBottomLeftRadius", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-bottom-left-radius", + subproperties: [ "border-bottom-left-radius" ], + }; + gCSSProperties["-webkit-border-bottom-right-radius"] = { + domProp: "webkitBorderBottomRightRadius", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "border-bottom-right-radius", + subproperties: [ "border-bottom-right-radius" ], + }; + gCSSProperties["-webkit-background-clip"] = { + domProp: "webkitBackgroundClip", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "background-clip", + subproperties: [ "background-clip" ], + }; + gCSSProperties["-webkit-background-origin"] = { + domProp: "webkitBackgroundOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "background-origin", + subproperties: [ "background-origin" ], + }; + gCSSProperties["-webkit-background-size"] = { + domProp: "webkitBackgroundSize", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "background-size", + subproperties: [ "background-size" ], + }; + gCSSProperties["-webkit-border-image"] = { + domProp: "webkitBorderImage", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "border-image", + subproperties: [ "border-image-source", "border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat" ], + }; + gCSSProperties["-webkit-box-shadow"] = { + domProp: "webkitBoxShadow", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "box-shadow", + subproperties: [ "box-shadow" ], + }; + gCSSProperties["-webkit-box-sizing"] = { + domProp: "webkitBoxSizing", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "box-sizing", + subproperties: [ "box-sizing" ], + }; + gCSSProperties["-webkit-box-flex"] = { + domProp: "webkitBoxFlex", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-flex", + subproperties: [ "-moz-box-flex" ], + }; + gCSSProperties["-webkit-box-ordinal-group"] = { + domProp: "webkitBoxOrdinalGroup", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-ordinal-group", + subproperties: [ "-moz-box-ordinal-group" ], + }; + gCSSProperties["-webkit-box-orient"] = { + domProp: "webkitBoxOrient", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-orient", + subproperties: [ "-moz-box-orient" ], + }; + gCSSProperties["-webkit-box-direction"] = { + domProp: "webkitBoxDirection", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-direction", + subproperties: [ "-moz-box-direction" ], + }; + gCSSProperties["-webkit-box-align"] = { + domProp: "webkitBoxAlign", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-align", + subproperties: [ "-moz-box-align" ], + }; + gCSSProperties["-webkit-box-pack"] = { + domProp: "webkitBoxPack", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-box-pack", + subproperties: [ "-moz-box-pack" ], + }; + gCSSProperties["-webkit-flex-direction"] = { + domProp: "webkitFlexDirection", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "flex-direction", + subproperties: [ "flex-direction" ], + }; + gCSSProperties["-webkit-flex-wrap"] = { + domProp: "webkitFlexWrap", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "flex-wrap", + subproperties: [ "flex-wrap" ], + }; + gCSSProperties["-webkit-flex-flow"] = { + domProp: "webkitFlexFlow", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "flex-flow", + subproperties: [ "flex-direction", "flex-wrap" ], + }; + gCSSProperties["-webkit-order"] = { + domProp: "webkitOrder", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "order", + subproperties: [ "order" ], + }; + gCSSProperties["-webkit-flex"] = { + domProp: "webkitFlex", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "flex", + subproperties: [ "flex-grow", "flex-shrink", "flex-basis" ], + }; + gCSSProperties["-webkit-flex-grow"] = { + domProp: "webkitFlexGrow", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "flex-grow", + subproperties: [ "flex-grow" ], + }; + gCSSProperties["-webkit-flex-shrink"] = { + domProp: "webkitFlexShrink", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "flex-shrink", + subproperties: [ "flex-shrink" ], + }; + gCSSProperties["-webkit-flex-basis"] = { + domProp: "webkitFlexBasis", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "flex-basis", + subproperties: [ "flex-basis" ], + }; + gCSSProperties["-webkit-justify-content"] = { + domProp: "webkitJustifyContent", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "justify-content", + subproperties: [ "justify-content" ], + }; + gCSSProperties["-webkit-align-items"] = { + domProp: "webkitAlignItems", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "align-items", + subproperties: [ "align-items" ], + }; + gCSSProperties["-webkit-align-self"] = { + domProp: "webkitAlignSelf", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "align-self", + subproperties: [ "align-self" ], + }; + gCSSProperties["-webkit-align-content"] = { + domProp: "webkitAlignContent", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "align-content", + subproperties: [ "align-content" ], + }; + gCSSProperties["-webkit-user-select"] = { + domProp: "webkitUserSelect", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-user-select", + subproperties: [ "-moz-user-select" ], + }; + + if (SupportsMaskShorthand()) { + gCSSProperties["-webkit-mask"] = { + domProp: "webkitMask", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + alias_for: "mask", + subproperties: [ "mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite" ], + }; + gCSSProperties["-webkit-mask-clip"] = { + domProp: "webkitMaskClip", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-clip", + subproperties: [ "mask-clip" ], + }; + + gCSSProperties["-webkit-mask-composite"] = { + domProp: "webkitMaskComposite", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-composite", + subproperties: [ "mask-composite" ], + }; + + gCSSProperties["-webkit-mask-image"] = { + domProp: "webkitMaskImage", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-image", + subproperties: [ "mask-image" ], + }; + gCSSProperties["-webkit-mask-origin"] = { + domProp: "webkitMaskOrigin", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-origin", + subproperties: [ "mask-origin" ], + }; + gCSSProperties["-webkit-mask-position"] = { + domProp: "webkitMaskPosition", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-position", + subproperties: [ "mask-position" ], + }; + gCSSProperties["-webkit-mask-position-x"] = { + domProp: "webkitMaskPositionX", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-position-x", + subproperties: [ "mask-position-x" ], + }; + gCSSProperties["-webkit-mask-position-y"] = { + domProp: "webkitMaskPositionY", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-position-y", + subproperties: [ "mask-position-y" ], + }; + gCSSProperties["-webkit-mask-repeat"] = { + domProp: "webkitMaskRepeat", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-repeat", + subproperties: [ "mask-repeat" ], + }; + gCSSProperties["-webkit-mask-size"] = { + domProp: "webkitMaskSize", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "mask-size", + subproperties: [ "mask-size" ], + }; + } +} + +if (IsCSSPropertyPrefEnabled("layout.css.unset-value.enabled")) { + gCSSProperties["animation"].invalid_values.push("2s unset"); + gCSSProperties["animation-direction"].invalid_values.push("normal, unset", "unset, normal"); + gCSSProperties["animation-name"].invalid_values.push("bounce, unset", "unset, bounce"); + gCSSProperties["-moz-border-bottom-colors"].invalid_values.push("red unset", "unset red"); + gCSSProperties["-moz-border-left-colors"].invalid_values.push("red unset", "unset red"); + gCSSProperties["border-radius"].invalid_values.push("unset 2px", "unset / 2px", "2px unset", "2px / unset"); + gCSSProperties["border-bottom-left-radius"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["border-bottom-right-radius"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["border-top-left-radius"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["border-top-right-radius"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["-moz-border-right-colors"].invalid_values.push("red unset", "unset red"); + gCSSProperties["-moz-border-top-colors"].invalid_values.push("red unset", "unset red"); + gCSSProperties["-moz-outline-radius"].invalid_values.push("unset 2px", "unset / 2px", "2px unset", "2px / unset"); + gCSSProperties["-moz-outline-radius-bottomleft"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["-moz-outline-radius-bottomright"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["-moz-outline-radius-topleft"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["-moz-outline-radius-topright"].invalid_values.push("unset 2px", "2px unset"); + gCSSProperties["background-image"].invalid_values.push("-moz-linear-gradient(unset, 10px 10px, from(blue))", "-moz-linear-gradient(unset, 10px 10px, blue 0)", "-moz-repeating-linear-gradient(unset, 10px 10px, blue 0)"); + gCSSProperties["box-shadow"].invalid_values.push("unset, 2px 2px", "2px 2px, unset", "inset unset"); + gCSSProperties["text-overflow"].invalid_values.push('"hello" unset', 'unset "hello"', 'clip unset', 'unset clip', 'unset inherit', 'unset none', 'initial unset'); + gCSSProperties["text-shadow"].invalid_values.push("unset, 2px 2px", "2px 2px, unset"); + gCSSProperties["transition"].invalid_values.push("2s unset"); + gCSSProperties["transition-property"].invalid_values.push("unset, color", "color, unset"); + if (IsCSSPropertyPrefEnabled("layout.css.filters.enabled")) { + gCSSProperties["filter"].invalid_values.push("drop-shadow(unset, 2px 2px)", "drop-shadow(2px 2px, unset)"); + } + if (IsCSSPropertyPrefEnabled("layout.css.text-align-unsafe-value.enabled")) { + gCSSProperties["text-align"].other_values.push("true left"); + } else { + gCSSProperties["text-align"].invalid_values.push("true left"); + } +} + +if (IsCSSPropertyPrefEnabled("layout.css.float-logical-values.enabled")) { + gCSSProperties["float"].other_values.push("inline-start"); + gCSSProperties["float"].other_values.push("inline-end"); + gCSSProperties["clear"].other_values.push("inline-start"); + gCSSProperties["clear"].other_values.push("inline-end"); +} else { + gCSSProperties["float"].invalid_values.push("inline-start"); + gCSSProperties["float"].invalid_values.push("inline-end"); + gCSSProperties["clear"].invalid_values.push("inline-start"); + gCSSProperties["clear"].invalid_values.push("inline-end"); +} + +if (IsCSSPropertyPrefEnabled("layout.css.background-clip-text.enabled")) { + gCSSProperties["background-clip"].other_values.push( + "text", + "content-box, text", + "text, border-box", + "text, text" + ); + gCSSProperties["background"].other_values.push( + "url(404.png) green padding-box text", + "content-box text url(404.png) blue" + ); +} else { + gCSSProperties["background-clip"].invalid_values.push( + "text", + "content-box, text", + "text, border-box", + "text, text" + ); + gCSSProperties["background"].invalid_values.push( + "url(404.png) green padding-box text", + "content-box text url(404.png) blue" + ); +} + +// Copy aliased properties' fields from their alias targets. +for (var prop in gCSSProperties) { + var entry = gCSSProperties[prop]; + if (entry.alias_for) { + var aliasTargetEntry = gCSSProperties[entry.alias_for]; + if (!aliasTargetEntry) { + ok(false, + "Alias '" + prop + "' alias_for field, '" + entry.alias_for + "', " + + "must be set to a recognized CSS property in gCSSProperties"); + } else { + // Copy 'values' fields & 'prerequisites' field from aliasTargetEntry: + var fieldsToCopy = + ["initial_values", "other_values", "invalid_values", + "quirks_values", "unbalanced_values", + "prerequisites"]; + + fieldsToCopy.forEach(function(fieldName) { + // (Don't copy the field if the alias already has something there, + // or if the aliased property doesn't have anything to copy.) + if (!(fieldName in entry) && + fieldName in aliasTargetEntry) { + entry[fieldName] = aliasTargetEntry[fieldName] + } + }); + } + } +} + +if (false) { + // TODO These properties are chrome-only, and are not exposed via CSSOM. + // We may still want to find a way to test them. See bug 1206999. + gCSSProperties["-moz-window-shadow"] = { + //domProp: "MozWindowShadow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "default" ], + other_values: [ "none", "menu", "tooltip", "sheet" ], + invalid_values: [] + }; +} diff --git a/layout/style/test/redirect.sjs b/layout/style/test/redirect.sjs new file mode 100644 index 000000000..b6249cadf --- /dev/null +++ b/layout/style/test/redirect.sjs @@ -0,0 +1,5 @@ +function handleRequest(request, response) +{ + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", request.queryString, false); +} diff --git a/layout/style/test/redundant_font_download.sjs b/layout/style/test/redundant_font_download.sjs new file mode 100644 index 000000000..7eb7d8f8d --- /dev/null +++ b/layout/style/test/redundant_font_download.sjs @@ -0,0 +1,60 @@ +const BinaryOutputStream = + Components.Constructor("@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream"); + +// this is simply a hex dump of a red square .PNG image +const RED_SQUARE = + [ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, + 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x20, 0x08, 0x02, 0x00, 0x00, 0x00, 0xFC, + 0x18, 0xED, 0xA3, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, + 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, 0x00, 0x28, + 0x49, 0x44, 0x41, 0x54, 0x48, 0xC7, 0xED, 0xCD, 0x41, 0x0D, + 0x00, 0x00, 0x08, 0x04, 0xA0, 0xD3, 0xFE, 0x9D, 0x35, 0x85, + 0x0F, 0x37, 0x28, 0x40, 0x4D, 0x6E, 0x75, 0x04, 0x02, 0x81, + 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0xC1, 0x93, 0x60, 0x01, + 0xA3, 0xC4, 0x01, 0x3F, 0x58, 0x1D, 0xEF, 0x27, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 + ]; + +function handleRequest(request, response) +{ + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + + response.setHeader("Cache-Control", "no-cache"); + + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + + var log = getState("bug-879963-request-log") || ""; + + var stream = new BinaryOutputStream(response.bodyOutputStream); + + if (query["q"] == "init") { + log = "init"; // initialize the log, and return a PNG image + response.setHeader("Content-Type", "image/png", false); + stream.writeByteArray(RED_SQUARE, RED_SQUARE.length); + } else if (query["q"] == "image") { + log = log + ";" + query["q"]; + response.setHeader("Content-Type", "image/png", false); + stream.writeByteArray(RED_SQUARE, RED_SQUARE.length); + } else if (query["q"] == "font") { + log = log + ";" + query["q"]; + // we don't provide a real font; that's ok, OTS will just reject it + response.write("Junk"); + } else if (query["q"] == "report") { + // don't include the actual "report" request in the log we return + response.write(log); + } else { + log = log + ";" + query["q"]; + response.setStatusLine(request.httpVersion, 404, "Not Found"); + } + + setState("bug-879963-request-log", log); +} diff --git a/layout/style/test/style_attribute_tests.js b/layout/style/test/style_attribute_tests.js new file mode 100644 index 000000000..be24baf95 --- /dev/null +++ b/layout/style/test/style_attribute_tests.js @@ -0,0 +1,27 @@ + +SimpleTest.waitForExplicitFinish(); + +window.addEventListener("load", runTests, false); + +function runTests(event) +{ + if (event.target != document) { + return; + } + + var elt = document.getElementById("content"); + + elt.setAttribute("style", "color: blue; background-color: fuchsia"); + is(elt.style.color, "blue", + "setting correct style attribute (color)"); + is(elt.style.backgroundColor, "fuchsia", + "setting correct style attribute (color)"); + + elt.setAttribute("style", "{color: blue; background-color: fuchsia}"); + is(elt.style.color, "", + "setting braced style attribute (color)"); + is(elt.style.backgroundColor, "", + "setting braced style attribute (color)"); + + SimpleTest.finish(); +} diff --git a/layout/style/test/support/external-variable-url.css b/layout/style/test/support/external-variable-url.css new file mode 100644 index 000000000..b13150428 --- /dev/null +++ b/layout/style/test/support/external-variable-url.css @@ -0,0 +1,3 @@ +div { + --a: url('image.png'); +} diff --git a/layout/style/test/test_acid3_test46.html b/layout/style/test/test_acid3_test46.html new file mode 100644 index 000000000..89850f0c5 --- /dev/null +++ b/layout/style/test/test_acid3_test46.html @@ -0,0 +1,141 @@ + + + + + + Test for Bug 156716 + + + + + +Mozilla Bug 156716 + +
    +
    +
    +

    + +

    + + + diff --git a/layout/style/test/test_addSheet.html b/layout/style/test/test_addSheet.html new file mode 100644 index 000000000..e112378a9 --- /dev/null +++ b/layout/style/test/test_addSheet.html @@ -0,0 +1,46 @@ + + + + Test for addSheet + + + + +Mozilla Bug 1024707 + + + + +
    +
    +
    +
    diff --git a/layout/style/test/test_additional_sheets.html b/layout/style/test/test_additional_sheets.html
    new file mode 100644
    index 000000000..f1b8f6ed8
    --- /dev/null
    +++ b/layout/style/test/test_additional_sheets.html
    @@ -0,0 +1,314 @@
    +
    +
    +
    +  Test for additional sheets
    +  
    +  
    +
    +
    +Mozilla Bug 737003
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_align_justify_computed_values.html b/layout/style/test/test_align_justify_computed_values.html new file mode 100644 index 000000000..3cd4b8b0e --- /dev/null +++ b/layout/style/test/test_align_justify_computed_values.html @@ -0,0 +1,529 @@ + + + + + + Test align/justify-items/self/content computed values + + + + +Mozilla Bug 696253 + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_align_shorthand_serialization.html b/layout/style/test/test_align_shorthand_serialization.html new file mode 100644 index 000000000..95a3f4814 --- /dev/null +++ b/layout/style/test/test_align_shorthand_serialization.html @@ -0,0 +1,123 @@ + + + + + Test serialization of CSS Align shorthand properties + + + + + + + + + + diff --git a/layout/style/test/test_all_shorthand.html b/layout/style/test/test_all_shorthand.html new file mode 100644 index 000000000..6185778cc --- /dev/null +++ b/layout/style/test/test_all_shorthand.html @@ -0,0 +1,159 @@ + +Test the 'all' shorthand property + + + + + + + +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations.html b/layout/style/test/test_animations.html new file mode 100644 index 000000000..eaccba122 --- /dev/null +++ b/layout/style/test/test_animations.html @@ -0,0 +1,2047 @@ + + + + + + Test for css3-animations (Bug 435442) + + + + + + +Mozilla Bug 435442 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_async_tests.html b/layout/style/test/test_animations_async_tests.html new file mode 100644 index 000000000..5d328c7aa --- /dev/null +++ b/layout/style/test/test_animations_async_tests.html @@ -0,0 +1,26 @@ + + + + + Test for Bug 1086937 + + + + + + +Mozilla Bug 1086937 +
    +
    +
    + + diff --git a/layout/style/test/test_animations_dynamic_changes.html b/layout/style/test/test_animations_dynamic_changes.html new file mode 100644 index 000000000..a68d734dc --- /dev/null +++ b/layout/style/test/test_animations_dynamic_changes.html @@ -0,0 +1,65 @@ + + + + + Test for Bug 978833 + + + + + +Mozilla Bug 978833 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_effect_timing_duration.html b/layout/style/test/test_animations_effect_timing_duration.html new file mode 100644 index 000000000..30d77f20a --- /dev/null +++ b/layout/style/test/test_animations_effect_timing_duration.html @@ -0,0 +1,24 @@ + + + + Test for animation.effect.timing on compositor + + + + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_effect_timing_enddelay.html b/layout/style/test/test_animations_effect_timing_enddelay.html new file mode 100644 index 000000000..d4ad918dd --- /dev/null +++ b/layout/style/test/test_animations_effect_timing_enddelay.html @@ -0,0 +1,24 @@ + + + + Test for animation.effect.timing.endDelay on compositor + + + + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_effect_timing_iterations.html b/layout/style/test/test_animations_effect_timing_iterations.html new file mode 100644 index 000000000..625c52867 --- /dev/null +++ b/layout/style/test/test_animations_effect_timing_iterations.html @@ -0,0 +1,24 @@ + + + + Test for animation.effect.timing.iterations on compositor + + + + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_event_handler_attribute.html b/layout/style/test/test_animations_event_handler_attribute.html new file mode 100644 index 000000000..e5def2b34 --- /dev/null +++ b/layout/style/test/test_animations_event_handler_attribute.html @@ -0,0 +1,147 @@ + + + + + + Test for CSS Animation and Transition event handler + attributes. (Bug 911987) + + + + + + +Mozilla Bug + 911987 +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_animations_event_order.html b/layout/style/test/test_animations_event_order.html
    new file mode 100644
    index 000000000..5af7639cc
    --- /dev/null
    +++ b/layout/style/test/test_animations_event_order.html
    @@ -0,0 +1,585 @@
    +
    +
    +
    +
    +
    +  
    +  Test for CSS Animation and Transition event ordering
    +         (Bug 1183461)
    +  
    +  
    +  
    +  
    +  
    +
    +
    +Mozilla Bug
    +  1183461
    +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_animations_iterationstart.html b/layout/style/test/test_animations_iterationstart.html
    new file mode 100644
    index 000000000..d6a54f3b2
    --- /dev/null
    +++ b/layout/style/test/test_animations_iterationstart.html
    @@ -0,0 +1,28 @@
    +
    +
    +
    +
    +  Test for iterationStart on compositor animations (Bug 1248338)
    +  
    +  
    +
    +
    +Mozilla Bug 1248338
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_omta.html b/layout/style/test/test_animations_omta.html new file mode 100644 index 000000000..4b276c896 --- /dev/null +++ b/layout/style/test/test_animations_omta.html @@ -0,0 +1,2392 @@ + + + + + + + Test for css3-animations running on the compositor thread (Bug + 964646) + + + + + + + +Mozilla Bug + 964646 +
    +
    +
    +
    diff --git a/layout/style/test/test_animations_omta_start.html b/layout/style/test/test_animations_omta_start.html
    new file mode 100644
    index 000000000..235c32342
    --- /dev/null
    +++ b/layout/style/test/test_animations_omta_start.html
    @@ -0,0 +1,189 @@
    +
    +
    +
    +
    +  
    +  Test OMTA animations start correctly (Bug 975261)
    +  
    +  
    +  
    +  
    +  
    +
    +
    +Mozilla Bug
    +  975261
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_pausing.html b/layout/style/test/test_animations_pausing.html new file mode 100644 index 000000000..65dee5b29 --- /dev/null +++ b/layout/style/test/test_animations_pausing.html @@ -0,0 +1,28 @@ + + + + + Test for play() and pause() on animations (Bug 1070745) + + + + +Mozilla Bug 1070745 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_playbackrate.html b/layout/style/test/test_animations_playbackrate.html new file mode 100644 index 000000000..16deca02c --- /dev/null +++ b/layout/style/test/test_animations_playbackrate.html @@ -0,0 +1,28 @@ + + + + + Test for Animation.playbackRate on compositor animations (Bug 1175751) + + + + +Mozilla Bug 1175751 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_styles_on_event.html b/layout/style/test/test_animations_styles_on_event.html new file mode 100644 index 000000000..da7680727 --- /dev/null +++ b/layout/style/test/test_animations_styles_on_event.html @@ -0,0 +1,28 @@ + + + + + Test that mouse movement immediately after finish() should involve restyling for finished state(Bug 1228137) + + + + +Mozilla Bug 1228137 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_animations_with_disabled_properties.html b/layout/style/test/test_animations_with_disabled_properties.html new file mode 100644 index 000000000..2ac631169 --- /dev/null +++ b/layout/style/test/test_animations_with_disabled_properties.html @@ -0,0 +1,34 @@ + + + + + Test CSS animations ignore disabled properties (Bug 1265611) + + + + +Mozilla Bug + 1265611 +
    +
    +
    + + diff --git a/layout/style/test/test_any_dynamic.html b/layout/style/test/test_any_dynamic.html new file mode 100644 index 000000000..ae3276d86 --- /dev/null +++ b/layout/style/test/test_any_dynamic.html @@ -0,0 +1,49 @@ + + + + + Test for Bug 544834 + + + + + +Mozilla Bug 544834 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_asyncopen2.html b/layout/style/test/test_asyncopen2.html new file mode 100644 index 000000000..6dda6848a --- /dev/null +++ b/layout/style/test/test_asyncopen2.html @@ -0,0 +1,54 @@ + + + + + Bug 1195173 - Test asyncOpen2 security exception + + + + + + + + +Mozilla Bug 1195173 +

    + + + + + diff --git a/layout/style/test/test_at_rule_parse_serialize.html b/layout/style/test/test_at_rule_parse_serialize.html new file mode 100644 index 000000000..3723e335b --- /dev/null +++ b/layout/style/test/test_at_rule_parse_serialize.html @@ -0,0 +1,43 @@ + + + + + Test for Bug 478160 + + + + + +Mozilla Bug 478160 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_attribute_selector_eof_behavior.html b/layout/style/test/test_attribute_selector_eof_behavior.html new file mode 100644 index 000000000..76635f9ed --- /dev/null +++ b/layout/style/test/test_attribute_selector_eof_behavior.html @@ -0,0 +1,18 @@ + + +Test for EOF behavior of attribute selectors in selectors API + + +
    + diff --git a/layout/style/test/test_background_blend_mode.html b/layout/style/test/test_background_blend_mode.html new file mode 100644 index 000000000..443fe1940 --- /dev/null +++ b/layout/style/test/test_background_blend_mode.html @@ -0,0 +1,58 @@ + + + + Test for miscellaneous computed style issues + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_box_size_keywords.html b/layout/style/test/test_box_size_keywords.html new file mode 100644 index 000000000..c8bd5e86c --- /dev/null +++ b/layout/style/test/test_box_size_keywords.html @@ -0,0 +1,172 @@ + + + + + Test for Bug 1122253 + + + + + +Mozilla Bug 1122253 + + + +
    +
    +
    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug1055933.html b/layout/style/test/test_bug1055933.html new file mode 100644 index 000000000..bce171682 --- /dev/null +++ b/layout/style/test/test_bug1055933.html @@ -0,0 +1,42 @@ + + + + + Test for Bug 1055933 + + + + + + +Mozilla Bug 1055933 +

    + +
    +
    +
    +
    + + + + +
    + + diff --git a/layout/style/test/test_bug1089417.html b/layout/style/test/test_bug1089417.html new file mode 100644 index 000000000..3b5a217e4 --- /dev/null +++ b/layout/style/test/test_bug1089417.html @@ -0,0 +1,47 @@ + + + + + + Test for Bug 1089417 + + + + + +Mozilla Bug 1089417 +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug1112014.html b/layout/style/test/test_bug1112014.html new file mode 100644 index 000000000..d0f66aa46 --- /dev/null +++ b/layout/style/test/test_bug1112014.html @@ -0,0 +1,127 @@ + + + + + + Test for Bug 1112014 + + + + + +Mozilla Bug 1112014 +

    + +
    +
    + + diff --git a/layout/style/test/test_bug1203766.html b/layout/style/test/test_bug1203766.html new file mode 100644 index 000000000..5d8152440 --- /dev/null +++ b/layout/style/test/test_bug1203766.html @@ -0,0 +1,112 @@ + + +Test for bug 1203766 + + + +Mozilla Bug 1203766 +

    +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_bug1232829.html b/layout/style/test/test_bug1232829.html new file mode 100644 index 000000000..8981d56e0 --- /dev/null +++ b/layout/style/test/test_bug1232829.html @@ -0,0 +1,38 @@ + + + + + +Test for Bug 1232829 + + + + + + + + diff --git a/layout/style/test/test_bug1292447.html b/layout/style/test/test_bug1292447.html new file mode 100644 index 000000000..9636780f4 --- /dev/null +++ b/layout/style/test/test_bug1292447.html @@ -0,0 +1,377 @@ + + + + + Test for Bug 1292447 + + + + + +Mozilla Bug 1292447 +

    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug160403.html b/layout/style/test/test_bug160403.html new file mode 100644 index 000000000..18ad66aa9 --- /dev/null +++ b/layout/style/test/test_bug160403.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 160403 + + + + +Mozilla Bug 160403 + +
    +
    +
    + + diff --git a/layout/style/test/test_bug200089.html b/layout/style/test/test_bug200089.html new file mode 100644 index 000000000..39ab78d59 --- /dev/null +++ b/layout/style/test/test_bug200089.html @@ -0,0 +1,30 @@ + + + + + Test for Bug 200089 + + + + +Mozilla Bug 200089 +
    + + +
    Cell
    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug221428.html b/layout/style/test/test_bug221428.html new file mode 100644 index 000000000..25965b567 --- /dev/null +++ b/layout/style/test/test_bug221428.html @@ -0,0 +1,68 @@ + + + + + Test for Bug 221428 + + + + + + + + +Mozilla Bug 221428 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug229915.html b/layout/style/test/test_bug229915.html new file mode 100644 index 000000000..e3d1f6280 --- /dev/null +++ b/layout/style/test/test_bug229915.html @@ -0,0 +1,95 @@ + + + + + Test for Bug 229915 + + + + + +Mozilla Bug 229915 +
    + +
    +

    After testing, this should turn green.

    +
    + +
    +

    To be replaced.

    +

    After testing, this should turn green.

    +
    + +
    +

    Previous paragraph.

    +

    To be removed.

    +

    After testing, this should turn green.

    +
    + +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug302186.html b/layout/style/test/test_bug302186.html new file mode 100644 index 000000000..746e7da0a --- /dev/null +++ b/layout/style/test/test_bug302186.html @@ -0,0 +1,508 @@ + + + + + Test for Bug 302186 + + + + + + + + + + +Mozilla Bug 302186 +

    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. + +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + + There should be no red. +
    +
    + + +
    +
    + + + There should be no red. +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + + There should be no red. +
    +
    + There should be no red. +
    +
    + + +
    +
    + + There should be no red. +
    +
    + There should be no red. + +
    +
    + + +
    +
    + There should be no red. +
    +
    + + +
    +
    + There should be no red. +
    +
    + +
    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug319381.html b/layout/style/test/test_bug319381.html new file mode 100644 index 000000000..f387428e8 --- /dev/null +++ b/layout/style/test/test_bug319381.html @@ -0,0 +1,89 @@ + + + + + Test for Bug 319381 + + + + +Mozilla Bug 319381 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug357614.html b/layout/style/test/test_bug357614.html new file mode 100644 index 000000000..2cfb63044 --- /dev/null +++ b/layout/style/test/test_bug357614.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 357614 + + + + + +Mozilla Bug 357614 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug363146.html b/layout/style/test/test_bug363146.html new file mode 100644 index 000000000..dfdc4028b --- /dev/null +++ b/layout/style/test/test_bug363146.html @@ -0,0 +1,62 @@ + + + + + Test for Bug 363146 + + + + +Mozilla Bug 363146 +
    + + + + + +
    Caption
    Cell
    +
    +
    + + + + + +
    Caption
    Cell
    +
    +

    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug372770.html b/layout/style/test/test_bug372770.html new file mode 100644 index 000000000..208a79e3e --- /dev/null +++ b/layout/style/test/test_bug372770.html @@ -0,0 +1,91 @@ + + + + + Test for Bug 372770 + + + + + +Mozilla Bug 372770 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug373293.html b/layout/style/test/test_bug373293.html new file mode 100644 index 000000000..07053303c --- /dev/null +++ b/layout/style/test/test_bug373293.html @@ -0,0 +1,29 @@ + + + + + Test for Bug 373293 + + + + +Mozilla Bug 373293 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug377947.html b/layout/style/test/test_bug377947.html new file mode 100644 index 000000000..43f60577c --- /dev/null +++ b/layout/style/test/test_bug377947.html @@ -0,0 +1,107 @@ + + + + + Test for Bug 377947 + + + + +Mozilla Bug 377947 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug379440.html b/layout/style/test/test_bug379440.html new file mode 100644 index 000000000..2a6c392ea --- /dev/null +++ b/layout/style/test/test_bug379440.html @@ -0,0 +1,72 @@ + + + + + Test for Bug 379440 + + + + + +Mozilla Bug 379440 +

    +

    +
    +
    +
    +
    +
    +
    +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug379741.html b/layout/style/test/test_bug379741.html new file mode 100644 index 000000000..fc99be1ff --- /dev/null +++ b/layout/style/test/test_bug379741.html @@ -0,0 +1,43 @@ + + + + + Test for Bug 379741 + + + + +Mozilla Bug 379741 + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug382027.html b/layout/style/test/test_bug382027.html new file mode 100644 index 000000000..f9a88b17f --- /dev/null +++ b/layout/style/test/test_bug382027.html @@ -0,0 +1,37 @@ + + + + + Test for Bug 382027 + + + + +Mozilla Bug 382027 + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug383075.html b/layout/style/test/test_bug383075.html new file mode 100644 index 000000000..c970a802f --- /dev/null +++ b/layout/style/test/test_bug383075.html @@ -0,0 +1,84 @@ + + + + + Test for bug 383075 + + + + + +Mozilla bug 383075 +

    + +The X'es below should have the same size:
    + +X +X +X +X +X +X +X +X +X + +
    + +X +X +X +X +X +X +X +X +X +

    + +
    +
    +
    + + + + diff --git a/layout/style/test/test_bug387615.html b/layout/style/test/test_bug387615.html new file mode 100644 index 000000000..daa61f342 --- /dev/null +++ b/layout/style/test/test_bug387615.html @@ -0,0 +1,53 @@ + + + + + Test for Bug 387615 + + + + + +Mozilla Bug 387615 +

    link

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug389464.html b/layout/style/test/test_bug389464.html new file mode 100644 index 000000000..05e2faf63 --- /dev/null +++ b/layout/style/test/test_bug389464.html @@ -0,0 +1,48 @@ + + + + + + + Test for preference not to use document colors + + + + + +Mozilla Bug 58048 +Mozilla Bug 255411 +
    + +
    text
    +

    text

    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_bug391034.html b/layout/style/test/test_bug391034.html new file mode 100644 index 000000000..e98f68f7a --- /dev/null +++ b/layout/style/test/test_bug391034.html @@ -0,0 +1,71 @@ + + + + + Test for Bug 391034 + + + + +Mozilla Bug 391034 +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug391221.html b/layout/style/test/test_bug391221.html new file mode 100644 index 000000000..f5cb9ce3b --- /dev/null +++ b/layout/style/test/test_bug391221.html @@ -0,0 +1,43 @@ + + + + + Test for Bug 391221 + + + + +Mozilla Bug 391221 +

    +

    +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug397427.html b/layout/style/test/test_bug397427.html new file mode 100644 index 000000000..3175d0e92 --- /dev/null +++ b/layout/style/test/test_bug397427.html @@ -0,0 +1,91 @@ + + + + + Test for Bug 397427 + + + + + + + + +Mozilla Bug 397427 +

    + + + +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug399349.html b/layout/style/test/test_bug399349.html new file mode 100644 index 000000000..8e61f7d3b --- /dev/null +++ b/layout/style/test/test_bug399349.html @@ -0,0 +1,80 @@ + + + + + Test for Bug 363146 + + + + + +Mozilla Bug 399349 + + +
    + + +
    +
    +
    + + +
    + + +
    + + +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_bug401046.html b/layout/style/test/test_bug401046.html new file mode 100644 index 000000000..34491eaa8 --- /dev/null +++ b/layout/style/test/test_bug401046.html @@ -0,0 +1,82 @@ + + + + + Test for Bug 401046 + + + + + + +Mozilla Bug 401046 +

    + 汉字 + 汉字 + 汉字 + 汉字 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug405818.html b/layout/style/test/test_bug405818.html new file mode 100644 index 000000000..fb1c98708 --- /dev/null +++ b/layout/style/test/test_bug405818.html @@ -0,0 +1,72 @@ + + + + + Test for Bug 405818 + + + + + + + + + + +Mozilla Bug 405818 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug412901.html b/layout/style/test/test_bug412901.html new file mode 100644 index 000000000..6443d25c3 --- /dev/null +++ b/layout/style/test/test_bug412901.html @@ -0,0 +1,42 @@ + + + + + Test for Bug 412901 + + + + +Mozilla Bug 412901 +
    +
    +

    +
    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug413958.html b/layout/style/test/test_bug413958.html new file mode 100644 index 000000000..273e4466e --- /dev/null +++ b/layout/style/test/test_bug413958.html @@ -0,0 +1,78 @@ + + + + + Test for Bug 413958 + + + + + + +

    Mozilla Bug 413958. All text below should be black on white.

    +

    Sheet: 1 + 2 + 3. + Style attr: 4. + Properties: 5.

    + + + diff --git a/layout/style/test/test_bug418986-2.html b/layout/style/test/test_bug418986-2.html new file mode 100644 index 000000000..aa9ff8af1 --- /dev/null +++ b/layout/style/test/test_bug418986-2.html @@ -0,0 +1,33 @@ + + + + + + Test 2/3 for Bug #418986: Resist fingerprinting by preventing exposure of screen and system info + + + + + + + + +Bug 418986 +

    TEST

    + +

    +
    +
    + + diff --git a/layout/style/test/test_bug437915.html b/layout/style/test/test_bug437915.html new file mode 100644 index 000000000..595c0a643 --- /dev/null +++ b/layout/style/test/test_bug437915.html @@ -0,0 +1,70 @@ + + + + + Test for Bug 437915 + + + + + +Mozilla Bug 437915 +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug450191.html b/layout/style/test/test_bug450191.html new file mode 100644 index 000000000..6471bcc73 --- /dev/null +++ b/layout/style/test/test_bug450191.html @@ -0,0 +1,64 @@ + + + + + Test for Bug 450191 + + + + +Mozilla Bug 450191 + +
    +
    +
    + + diff --git a/layout/style/test/test_bug453896_deck.html b/layout/style/test/test_bug453896_deck.html new file mode 100644 index 000000000..603063241 --- /dev/null +++ b/layout/style/test/test_bug453896_deck.html @@ -0,0 +1,42 @@ + + + + + Test for Bug 453896 + + + + +Mozilla Bug 453896 +
    + +
    + + + +
    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug470769.html b/layout/style/test/test_bug470769.html new file mode 100644 index 000000000..cb32f47ec --- /dev/null +++ b/layout/style/test/test_bug470769.html @@ -0,0 +1,31 @@ + + + + + Test for Bug 470769 + + + + +Mozilla Bug 470769 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug499655.html b/layout/style/test/test_bug499655.html new file mode 100644 index 000000000..385ee43fa --- /dev/null +++ b/layout/style/test/test_bug499655.html @@ -0,0 +1,45 @@ + + + + Test for Bug 499655 + + + + +Mozilla Bug 499655 +

    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_bug499655.xhtml b/layout/style/test/test_bug499655.xhtml new file mode 100644 index 000000000..539c3fa36 --- /dev/null +++ b/layout/style/test/test_bug499655.xhtml @@ -0,0 +1,48 @@ + + + + Test for Bug 499655 + + + + +Mozilla Bug 499655 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug511909.html b/layout/style/test/test_bug511909.html new file mode 100644 index 000000000..060636a9f --- /dev/null +++ b/layout/style/test/test_bug511909.html @@ -0,0 +1,205 @@ + + +@media and @-moz-document testcases + + + + + + + + + + + + + + + + + + + + + + + + Mozilla Bug 511909 +

    + + + +
    +
    default style
    +
    +This line should be pink
    + +This line should be green
    + +This line should be blue
    + +
    @-moz-document {...}
    +
    +This line should be pink
    +
    @media screen {...}
    +
    +This line should be green
    +
    @-moz-document {
    +   @media screen {...}
    +}
    +
    +This line should be blue
    +
    @media print {
    +  @-moz-document regexp("not_this_url"),}
    +    #mx {
    +        color: pink;
    +    }
    +  }
    +}
    +
    +This line should be pink
    +
    @-moz-document regexp("not_this_url"){
    +  @media print ,}
    +    #mxx {
    +      color: blue;
    +    }
    +  }
    +}
    +
    +This line should be blue
    +
    @media screen {
    +  @-moz-documen {...}
    +}
    +
    +This line should be green
    +
    @media screen {
    +  @-moz-document {
    +    @media screen {...}
    +  }
    +}
    +
    +This line should be blue
    + + + diff --git a/layout/style/test/test_bug517224.html b/layout/style/test/test_bug517224.html new file mode 100644 index 000000000..e748f9e20 --- /dev/null +++ b/layout/style/test/test_bug517224.html @@ -0,0 +1,45 @@ + + + + + Test for Bug 517224 + + + + + + +Mozilla Bug 517224 +

    Element with overridden background

    +
    +
    +
    + + diff --git a/layout/style/test/test_bug524175.html b/layout/style/test/test_bug524175.html new file mode 100644 index 000000000..a86b3ed0b --- /dev/null +++ b/layout/style/test/test_bug524175.html @@ -0,0 +1,28 @@ + + + + + Test for Bug 524175 + + + + + +Mozilla Bug 524175 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug525952.html b/layout/style/test/test_bug525952.html new file mode 100644 index 000000000..66768a742 --- /dev/null +++ b/layout/style/test/test_bug525952.html @@ -0,0 +1,46 @@ + + + + + Test for Bug 525952 + + + + +Mozilla Bug 525952 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug534804.html b/layout/style/test/test_bug534804.html new file mode 100644 index 000000000..ac04c3563 --- /dev/null +++ b/layout/style/test/test_bug534804.html @@ -0,0 +1,89 @@ + + + + + Test for Bug 534804 + + + + + + +Mozilla Bug 534804 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_bug573255.html b/layout/style/test/test_bug573255.html new file mode 100644 index 000000000..5ea1d826f --- /dev/null +++ b/layout/style/test/test_bug573255.html @@ -0,0 +1,32 @@ + + + + + Test for Bug 573255 + + + + + +Mozilla Bug 573255 +

    +
    +
    +
    + + diff --git a/layout/style/test/test_bug580685.html b/layout/style/test/test_bug580685.html new file mode 100644 index 000000000..795b2ce0c --- /dev/null +++ b/layout/style/test/test_bug580685.html @@ -0,0 +1,41 @@ + + + + + Test for Bug 580685 + + + + +Mozilla Bug 580685 +

    + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug621351.html b/layout/style/test/test_bug621351.html new file mode 100644 index 000000000..6a6d373af --- /dev/null +++ b/layout/style/test/test_bug621351.html @@ -0,0 +1,35 @@ + + +Test for Bug 160403 + + + + + diff --git a/layout/style/test/test_bug635286.html b/layout/style/test/test_bug635286.html new file mode 100644 index 000000000..c6c9d6ad4 --- /dev/null +++ b/layout/style/test/test_bug635286.html @@ -0,0 +1,52 @@ + + + + + Test for Bug 635286 + + + + + + +Mozilla Bug 635286 +
    case1, :-moz-any()
    +
    case2, :not()
    +
    case3, :not() in :-moz-any()
    +
    case4, :-moz-any() in :not()
    +
    +
    +
    + + diff --git a/layout/style/test/test_bug645998.html b/layout/style/test/test_bug645998.html new file mode 100644 index 000000000..f511185ed --- /dev/null +++ b/layout/style/test/test_bug645998.html @@ -0,0 +1,29 @@ + + + + + Test for Bug 645998 + + + + + + + +Mozilla Bug 645998 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug652486.html b/layout/style/test/test_bug652486.html new file mode 100644 index 000000000..9abb7041c --- /dev/null +++ b/layout/style/test/test_bug652486.html @@ -0,0 +1,212 @@ + + + + + Test for Bug 652486 and Bug 1039488 + + + + +Mozilla Bug 652486 +Mozilla Bug 1039488 + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug657143.html b/layout/style/test/test_bug657143.html new file mode 100644 index 000000000..4b9980eb7 --- /dev/null +++ b/layout/style/test/test_bug657143.html @@ -0,0 +1,132 @@ + + + + + Test for Bug 657143 + + + + + +Mozilla Bug 657143 +

    + + +
    +
    +
    + + diff --git a/layout/style/test/test_bug664955.html b/layout/style/test/test_bug664955.html new file mode 100644 index 000000000..a7e31c557 --- /dev/null +++ b/layout/style/test/test_bug664955.html @@ -0,0 +1,37 @@ + + + + + Test for Bug 664955 + + + + + +Mozilla Bug 664955 +

    + + + + +

    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug667520.html b/layout/style/test/test_bug667520.html new file mode 100644 index 000000000..b6909eedb --- /dev/null +++ b/layout/style/test/test_bug667520.html @@ -0,0 +1,50 @@ + + + + + Test for Bug 667520 + + + + +Mozilla Bug 667520 +

    + + + + + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug716226.html b/layout/style/test/test_bug716226.html new file mode 100644 index 000000000..25df9ffd8 --- /dev/null +++ b/layout/style/test/test_bug716226.html @@ -0,0 +1,52 @@ + + + + + + Test for Bug 716226 + + + + + +Mozilla Bug 716226 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug732153.html b/layout/style/test/test_bug732153.html new file mode 100644 index 000000000..892029886 --- /dev/null +++ b/layout/style/test/test_bug732153.html @@ -0,0 +1,22 @@ + + +Test for Bug 732153 + + +Mozilla Bug 732153 +
    + diff --git a/layout/style/test/test_bug732209.html b/layout/style/test/test_bug732209.html new file mode 100644 index 000000000..0a6c4353d --- /dev/null +++ b/layout/style/test/test_bug732209.html @@ -0,0 +1,95 @@ + + + + + + Test for Bug 732209 + + + + + + + + + + + + + + + + + +Mozilla Bug 732209 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug73586.html b/layout/style/test/test_bug73586.html new file mode 100644 index 000000000..a88ae0a30 --- /dev/null +++ b/layout/style/test/test_bug73586.html @@ -0,0 +1,192 @@ + + + + + Test for Bug 73586 + + + + + +Mozilla Bug 73586 +
    + + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug74880.html b/layout/style/test/test_bug74880.html new file mode 100644 index 000000000..9641f5944 --- /dev/null +++ b/layout/style/test/test_bug74880.html @@ -0,0 +1,125 @@ + + + + + Test for Bug 74880 + + + + + +Mozilla Bug 74880 +
    +

    +
    + +
    +
    +
    + + + diff --git a/layout/style/test/test_bug765590.html b/layout/style/test/test_bug765590.html new file mode 100644 index 000000000..1e2445804 --- /dev/null +++ b/layout/style/test/test_bug765590.html @@ -0,0 +1,21 @@ + + + + + Test for Bug 765590 + + + + + + + diff --git a/layout/style/test/test_bug771043.html b/layout/style/test/test_bug771043.html new file mode 100644 index 000000000..ca9d0cfb6 --- /dev/null +++ b/layout/style/test/test_bug771043.html @@ -0,0 +1,69 @@ + + + + + + Test for Bug 771043 + + + + + +Mozilla Bug 771043 + +

    +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_bug798567.html b/layout/style/test/test_bug798567.html new file mode 100644 index 000000000..b8e5dd0ea --- /dev/null +++ b/layout/style/test/test_bug798567.html @@ -0,0 +1,26 @@ + + + + + Test for Bug 798567 + + + + +
    bar
    +
    foobar
    + + + diff --git a/layout/style/test/test_bug798843_pref.html b/layout/style/test/test_bug798843_pref.html new file mode 100644 index 000000000..f403741c1 --- /dev/null +++ b/layout/style/test/test_bug798843_pref.html @@ -0,0 +1,57 @@ + + + + + Test that SVG glyph context-* values can be pref'ed off + + + + + + + + diff --git a/layout/style/test/test_bug829816.html b/layout/style/test/test_bug829816.html new file mode 100644 index 000000000..df6100e29 --- /dev/null +++ b/layout/style/test/test_bug829816.html @@ -0,0 +1,56 @@ + + + + + + Test for Bug 829816 + + + + + + + + + + + +Mozilla Bug 829816 +

    + +
    +
    + + diff --git a/layout/style/test/test_bug874919.html b/layout/style/test/test_bug874919.html new file mode 100644 index 000000000..df9facd74 --- /dev/null +++ b/layout/style/test/test_bug874919.html @@ -0,0 +1,55 @@ + + + + + Test for Bug 874919 + + + + + +Mozilla Bug 874919 +

    +
    + + + + + + +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_bug887741_at-rules_in_declaration_lists.html b/layout/style/test/test_bug887741_at-rules_in_declaration_lists.html new file mode 100644 index 000000000..b8896999e --- /dev/null +++ b/layout/style/test/test_bug887741_at-rules_in_declaration_lists.html @@ -0,0 +1,75 @@ + + + + + + Test for Bug 887741: at-rules in declaration lists + + + + + + +Mozilla Bug 887741 +

    + +
    +  
    +
    + + diff --git a/layout/style/test/test_bug892929.html b/layout/style/test/test_bug892929.html new file mode 100644 index 000000000..a67db56ee --- /dev/null +++ b/layout/style/test/test_bug892929.html @@ -0,0 +1,74 @@ + + + + + Bug 892929 test + + + + + + + + +
    +
    
    +
    +
    +
    +
    diff --git a/layout/style/test/test_bug98997.html b/layout/style/test/test_bug98997.html
    new file mode 100644
    index 000000000..c7104c7c4
    --- /dev/null
    +++ b/layout/style/test/test_bug98997.html
    @@ -0,0 +1,144 @@
    +
    +
    +
    +
    +  Test for Bug 98997
    +  
    +  
    +  
    +
    +
    +Mozilla Bug 98997
    +
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    +
    x
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    + + diff --git a/layout/style/test/test_cascade.html b/layout/style/test/test_cascade.html new file mode 100644 index 000000000..0a5d27a8b --- /dev/null +++ b/layout/style/test/test_cascade.html @@ -0,0 +1,91 @@ + + + + + + Test for Author style sheet aspects of CSS cascading + + + + + +Mozilla Bug +
    +
    +
    +
    + + + diff --git a/layout/style/test/test_ch_ex_no_infloops.html b/layout/style/test/test_ch_ex_no_infloops.html new file mode 100644 index 000000000..e8684e935 --- /dev/null +++ b/layout/style/test/test_ch_ex_no_infloops.html @@ -0,0 +1,61 @@ + + + + + Test for Bug 678671 + + + + + + +Mozilla Bug 678671 +

    +
    + + + + diff --git a/layout/style/test/test_change_hint_optimizations.html b/layout/style/test/test_change_hint_optimizations.html new file mode 100644 index 000000000..7de8d2431 --- /dev/null +++ b/layout/style/test/test_change_hint_optimizations.html @@ -0,0 +1,57 @@ + + + + + Test for style change hint optimizations + + + + + + + +
    +
    + + diff --git a/layout/style/test/test_clip-path_polygon.html b/layout/style/test/test_clip-path_polygon.html new file mode 100644 index 000000000..9f25accd1 --- /dev/null +++ b/layout/style/test/test_clip-path_polygon.html @@ -0,0 +1,41 @@ + + + +clip-path with polygon() hit test + + + +
    +

    + + \ No newline at end of file diff --git a/layout/style/test/test_compute_data_with_start_struct.html b/layout/style/test/test_compute_data_with_start_struct.html new file mode 100644 index 000000000..fab111e34 --- /dev/null +++ b/layout/style/test/test_compute_data_with_start_struct.html @@ -0,0 +1,109 @@ + + + + Test for correct handling of aStartStruct parameter to nsRuleNode::Compute*Data + + + + + + +Mozilla Bug 216456 +

    + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_computed_style.html b/layout/style/test/test_computed_style.html new file mode 100644 index 000000000..938a3fcf5 --- /dev/null +++ b/layout/style/test/test_computed_style.html @@ -0,0 +1,413 @@ + + + + Test for miscellaneous computed style issues + + + + +Mozilla Bug +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_computed_style_min_size_auto.html b/layout/style/test/test_computed_style_min_size_auto.html new file mode 100644 index 000000000..762172ee3 --- /dev/null +++ b/layout/style/test/test_computed_style_min_size_auto.html @@ -0,0 +1,133 @@ + + + + + + Test behavior of 'min-height:auto' and 'min-width:auto' (Bug 763689 and Bug 1304636) + + + + +Mozilla Bug 763689 +Mozilla Bug 1304636 + +
    +
    abc
    + +
    +
    abc
    +
    def
    +
    + +
    +
    abc
    +
    def
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + diff --git a/layout/style/test/test_computed_style_no_pseudo.html b/layout/style/test/test_computed_style_no_pseudo.html new file mode 100644 index 000000000..11ae16c75 --- /dev/null +++ b/layout/style/test/test_computed_style_no_pseudo.html @@ -0,0 +1,44 @@ + + + + + Test for Bug 505515 + + + + + +Mozilla Bug 505515 +

    This is some text in which the first line is in a different color.

    +
    +
    +
    + + diff --git a/layout/style/test/test_computed_style_prefs.html b/layout/style/test/test_computed_style_prefs.html new file mode 100644 index 000000000..163176237 --- /dev/null +++ b/layout/style/test/test_computed_style_prefs.html @@ -0,0 +1,99 @@ + + + + Test that preffed off properties do not appear in computed style + + + + +Mozilla Bug 919594 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_condition_text.html b/layout/style/test/test_condition_text.html new file mode 100644 index 000000000..9ab60758d --- /dev/null +++ b/layout/style/test/test_condition_text.html @@ -0,0 +1,93 @@ + + + + + Test for Bug 814907 + + + + + +Mozilla Bug 814907 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_condition_text_assignment.html b/layout/style/test/test_condition_text_assignment.html new file mode 100644 index 000000000..dc1ef923d --- /dev/null +++ b/layout/style/test/test_condition_text_assignment.html @@ -0,0 +1,59 @@ + + + + + Test for Bug 815021 + + + + + +Mozilla Bug 815021 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_contain_formatting_context.html b/layout/style/test/test_contain_formatting_context.html new file mode 100644 index 000000000..928cc35f5 --- /dev/null +++ b/layout/style/test/test_contain_formatting_context.html @@ -0,0 +1,40 @@ + + + + + + Test that 'contain: paint' updates 'display' correctly + + + + + +
    + + + diff --git a/layout/style/test/test_counter_descriptor_storage.html b/layout/style/test/test_counter_descriptor_storage.html new file mode 100644 index 000000000..8262d8017 --- /dev/null +++ b/layout/style/test/test_counter_descriptor_storage.html @@ -0,0 +1,267 @@ + + + + Test for parsing, storage and serialization of CSS @counter-style descriptor values + + + + +Mozilla Bug 966166 +
    +
    +
    +
    + + diff --git a/layout/style/test/test_counter_style.html b/layout/style/test/test_counter_style.html new file mode 100644 index 000000000..c248494f5 --- /dev/null +++ b/layout/style/test/test_counter_style.html @@ -0,0 +1,121 @@ + + + + + Test for css3-counter-style (Bug 966166) + + + + + + +Mozilla Bug 966166 +
    +

    +

    +

    +

    +
    +
    +
    + + diff --git a/layout/style/test/test_css_cross_domain.html b/layout/style/test/test_css_cross_domain.html new file mode 100644 index 000000000..055398dec --- /dev/null +++ b/layout/style/test/test_css_cross_domain.html @@ -0,0 +1,99 @@ + + + + + Test cross-domain CSS loading + + + + + +Mozilla + Bug 524223 + +
    + +
    +

     

    +
    1. text/css
      1. same origin
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      2. +
      3. cross origin
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      4. +
      5. same to cross
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      6. +
      7. cross to same
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
    2. +
    3. text/html
      1. same origin
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      2. +
      3. cross origin
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      4. +
      5. same to cross
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
      6. +
      7. cross to same
        1. valid
        2. +
        3. malformed
        4. +
        5. http error
    4. +
    +
    + +
    +

    Quirks

    + +
    + +
    +

    Standards

    + +
    + + + + diff --git a/layout/style/test/test_css_eof_handling.html b/layout/style/test/test_css_eof_handling.html new file mode 100644 index 000000000..b54b031da --- /dev/null +++ b/layout/style/test/test_css_eof_handling.html @@ -0,0 +1,278 @@ + + + + Test for CSS EOF handling + + + + +

    bug 311616, +bug 325064

    + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_css_escape_api.html b/layout/style/test/test_css_escape_api.html new file mode 100644 index 000000000..00ec240c7 --- /dev/null +++ b/layout/style/test/test_css_escape_api.html @@ -0,0 +1,94 @@ + + + + + + Test for Bug 955860 + + + + + +Mozilla Bug 955860 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_css_function_mismatched_parenthesis.html b/layout/style/test/test_css_function_mismatched_parenthesis.html new file mode 100644 index 000000000..b28c88c86 --- /dev/null +++ b/layout/style/test/test_css_function_mismatched_parenthesis.html @@ -0,0 +1,63 @@ + + + + + Test for Bug 897094 + + + + + +Mozilla Bug 897094 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_css_loader_crossorigin_data_url.html b/layout/style/test/test_css_loader_crossorigin_data_url.html new file mode 100644 index 000000000..67105d61f --- /dev/null +++ b/layout/style/test/test_css_loader_crossorigin_data_url.html @@ -0,0 +1,17 @@ + + +Test for handling of 'crossorigin' attribute on CSS link with data: URL + + + +
    +
    + diff --git a/layout/style/test/test_css_supports.html b/layout/style/test/test_css_supports.html new file mode 100644 index 000000000..fa5b3fdcb --- /dev/null +++ b/layout/style/test/test_css_supports.html @@ -0,0 +1,134 @@ + + + + + Test for Bug 779917 + + + + +Mozilla Bug 779917 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_css_supports_variables.html b/layout/style/test/test_css_supports_variables.html new file mode 100644 index 000000000..25618bdf6 --- /dev/null +++ b/layout/style/test/test_css_supports_variables.html @@ -0,0 +1,247 @@ + + + + + Test for Bug 773296 + + + + +Mozilla Bug 773296 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_csslexer.js b/layout/style/test/test_csslexer.js new file mode 100644 index 000000000..a71c02d8f --- /dev/null +++ b/layout/style/test/test_csslexer.js @@ -0,0 +1,171 @@ +/* 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/. + */ + +function test_lexer(domutils, cssText, tokenTypes) { + let lexer = domutils.getCSSLexer(cssText); + let reconstructed = ''; + let lastTokenEnd = 0; + let i = 0; + while (true) { + let token = lexer.nextToken(); + if (!token) { + break; + } + let combined = token.tokenType; + if (token.text) + combined += ":" + token.text; + equal(combined, tokenTypes[i]); + ok(token.endOffset > token.startOffset); + equal(token.startOffset, lastTokenEnd); + lastTokenEnd = token.endOffset; + reconstructed += cssText.substring(token.startOffset, token.endOffset); + ++i; + } + // Ensure that we saw the correct number of tokens. + equal(i, tokenTypes.length); + // Ensure that the reported offsets cover all the text. + equal(reconstructed, cssText); +} + +var LEX_TESTS = [ + ["simple", ["ident:simple"]], + ["simple: { hi; }", + ["ident:simple", "symbol::", + "whitespace", "symbol:{", + "whitespace", "ident:hi", + "symbol:;", "whitespace", + "symbol:}"]], + ["/* whatever */", ["comment"]], + ["'string'", ["string:string"]], + ['"string"', ["string:string"]], + ["rgb(1,2,3)", ["function:rgb", "number", + "symbol:,", "number", + "symbol:,", "number", + "symbol:)"]], + ["@media", ["at:media"]], + ["#hibob", ["id:hibob"]], + ["#123", ["hash:123"]], + ["23px", ["dimension:px"]], + ["23%", ["percentage"]], + ["url(http://example.com)", ["url:http://example.com"]], + ["url('http://example.com')", ["url:http://example.com"]], + ["url( 'http://example.com' )", + ["url:http://example.com"]], + // In CSS Level 3, this is an ordinary URL, not a BAD_URL. + ["url(http://example.com", ["url:http://example.com"]], + // See bug 1153981 to understand why this gets a SYMBOL token. + ["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]], + ["quo\\ting", ["ident:quoting"]], + ["'bad string\n", ["bad_string:bad string", "whitespace"]], + ["~=", ["includes"]], + ["|=", ["dashmatch"]], + ["^=", ["beginsmatch"]], + ["$=", ["endsmatch"]], + ["*=", ["containsmatch"]], + + // URANGE may be on the way out, and it isn't used by devutils, so + // let's skip it. + + ["", ["htmlcomment", "whitespace", "ident:html", + "whitespace", "ident:comment", "whitespace", + "htmlcomment"]], + + // earlier versions of CSS had "bad comment" tokens, but in level 3, + // unterminated comments are just comments. + ["/* bad comment", ["comment"]] +]; + +function test_lexer_linecol(domutils, cssText, locations) { + let lexer = domutils.getCSSLexer(cssText); + let i = 0; + while (true) { + let token = lexer.nextToken(); + let startLine = lexer.lineNumber; + let startColumn = lexer.columnNumber; + + // We do this in a bit of a funny way so that we can also test the + // location of the EOF. + let combined = ":" + startLine + ":" + startColumn; + if (token) + combined = token.tokenType + combined; + + equal(combined, locations[i]); + ++i; + + if (!token) { + break; + } + } + // Ensure that we saw the correct number of tokens. + equal(i, locations.length); +} + +function test_lexer_eofchar(domutils, cssText, argText, expectedAppend, + expectedNoAppend) { + let lexer = domutils.getCSSLexer(cssText); + while (lexer.nextToken()) { + // Nothing. + } + + do_print("EOF char test, input = " + cssText); + + let result = lexer.performEOFFixup(argText, true); + equal(result, expectedAppend); + + result = lexer.performEOFFixup(argText, false); + equal(result, expectedNoAppend); +} + +var LINECOL_TESTS = [ + ["simple", ["ident:0:0", ":0:6"]], + ["\n stuff", ["whitespace:0:0", "ident:1:4", ":1:9"]], + ['"string with \\\nnewline" \r\n', ["string:0:0", "whitespace:1:8", + ":2:0"]] +]; + +var EOFCHAR_TESTS = [ + ["hello", "hello"], + ["hello \\", "hello \\\\", "hello \\\uFFFD"], + ["'hello", "'hello'"], + ["\"hello", "\"hello\""], + ["'hello\\", "'hello\\\\'", "'hello'"], + ["\"hello\\", "\"hello\\\\\"", "\"hello\""], + ["/*hello", "/*hello*/"], + ["/*hello*", "/*hello*/"], + ["/*hello\\", "/*hello\\*/"], + ["url(hello", "url(hello)"], + ["url('hello", "url('hello')"], + ["url(\"hello", "url(\"hello\")"], + ["url(hello\\", "url(hello\\\\)", "url(hello\\\uFFFD)"], + ["url('hello\\", "url('hello\\\\')", "url('hello')"], + ["url(\"hello\\", "url(\"hello\\\\\")", "url(\"hello\")"], +]; + +function run_test() +{ + let domutils = Components.classes["@mozilla.org/inspector/dom-utils;1"] + .getService(Components.interfaces.inIDOMUtils); + + let text, result; + for ([text, result] of LEX_TESTS) { + test_lexer(domutils, text, result); + } + + for ([text, result] of LINECOL_TESTS) { + test_lexer_linecol(domutils, text, result); + } + + for ([text, expectedAppend, expectedNoAppend] of EOFCHAR_TESTS) { + if (!expectedNoAppend) { + expectedNoAppend = expectedAppend; + } + test_lexer_eofchar(domutils, text, text, expectedAppend, expectedNoAppend); + } + + // Ensure that passing a different inputString to performEOFFixup + // doesn't cause an assertion trying to strip a backslash from the + // end of an empty string. + test_lexer_eofchar(domutils, "'\\", "", "\\'", "'"); +} diff --git a/layout/style/test/test_default_bidi_css.html b/layout/style/test/test_default_bidi_css.html new file mode 100644 index 000000000..bb4db8653 --- /dev/null +++ b/layout/style/test/test_default_bidi_css.html @@ -0,0 +1,79 @@ + + + + + Test for Bug + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_default_computed_style.html b/layout/style/test/test_default_computed_style.html new file mode 100644 index 000000000..60f3dcab0 --- /dev/null +++ b/layout/style/test/test_default_computed_style.html @@ -0,0 +1,58 @@ + + + + + + Test for Bug 800983 + + + + + +Mozilla Bug 800983 +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_descriptor_storage.html b/layout/style/test/test_descriptor_storage.html new file mode 100644 index 000000000..50017f642 --- /dev/null +++ b/layout/style/test/test_descriptor_storage.html @@ -0,0 +1,119 @@ + + + + + Test for parsing, storage, and serialization of CSS @font-face descriptor values + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_descriptor_syntax_errors.html b/layout/style/test/test_descriptor_syntax_errors.html new file mode 100644 index 000000000..952625c92 --- /dev/null +++ b/layout/style/test/test_descriptor_syntax_errors.html @@ -0,0 +1,53 @@ + + + + + Test that we reject syntax errors listed in descriptor_database.js + + + + + +

    + +
    +
    +
    + + diff --git a/layout/style/test/test_dont_use_document_colors.html b/layout/style/test/test_dont_use_document_colors.html new file mode 100644 index 000000000..4ea47c88b --- /dev/null +++ b/layout/style/test/test_dont_use_document_colors.html @@ -0,0 +1,186 @@ + + + + + Test for preference not to use document colors + + + + + +Mozilla Bug 58048 +Mozilla Bug 255411 +
    + +
    Hello
    +
    Hello
    + + +
    Hello
    +
    Hello
    +
    Hello
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_dynamic_change_causing_reflow.html b/layout/style/test/test_dynamic_change_causing_reflow.html new file mode 100644 index 000000000..a941191f6 --- /dev/null +++ b/layout/style/test/test_dynamic_change_causing_reflow.html @@ -0,0 +1,173 @@ + + + + + + Test for Bug 1131371 + + + + +Mozilla Bug 1131371 +
    +
    +
    +
    +
    +
    +
    + + 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 000000000..937afa7b8 --- /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 000000000..8a94a0515 --- /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_flexbox_child_display_values.xhtml b/layout/style/test/test_flexbox_child_display_values.xhtml new file mode 100644 index 000000000..08308d38e --- /dev/null +++ b/layout/style/test/test_flexbox_child_display_values.xhtml @@ -0,0 +1,189 @@ + + + + + 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 000000000..ef6fd901d --- /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 000000000..24bffed94 --- /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_layout.html b/layout/style/test/test_flexbox_layout.html new file mode 100644 index 000000000..c484030be --- /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 000000000..49552c645 --- /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 000000000..f838fed0e --- /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 000000000..36d1b8560 --- /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 000000000..f00983b31 --- /dev/null +++ b/layout/style/test/test_flexbox_reflow_counts.html @@ -0,0 +1,137 @@ + + + + + + Test for Bug 1142686 + + + + + +Mozilla Bug 1142686 +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + 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 000000000..75eba7293 --- /dev/null +++ b/layout/style/test/test_font_face_parser.html @@ -0,0 +1,382 @@ + + + + + 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 000000000..526491ebc
    --- /dev/null
    +++ b/layout/style/test/test_font_family_parsing.html
    @@ -0,0 +1,276 @@
    +
    +
    +
    +  
    +  Font family name parsing tests
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    
    +
    +
    +
    +
    +
    diff --git a/layout/style/test/test_font_feature_values_parsing.html b/layout/style/test/test_font_feature_values_parsing.html
    new file mode 100644
    index 000000000..a5d4a6834
    --- /dev/null
    +++ b/layout/style/test/test_font_feature_values_parsing.html
    @@ -0,0 +1,356 @@
    +
    +
    +
    +  
    +  @font-feature-values rule parsing tests
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +
    
    +
    +
    +
    +
    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 000000000..26c5a2a85
    --- /dev/null
    +++ b/layout/style/test/test_font_loading_api.html
    @@ -0,0 +1,1897 @@
    +
    +
    +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 000000000..5be02a295 --- /dev/null +++ b/layout/style/test/test_garbage_at_end_of_declarations.html @@ -0,0 +1,151 @@ + + + + + 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 000000000..de77fec34 --- /dev/null +++ b/layout/style/test/test_grid_computed_values.html @@ -0,0 +1,106 @@ + + + + + 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 000000000..741404bc4 --- /dev/null +++ b/layout/style/test/test_grid_container_shorthands.html @@ -0,0 +1,282 @@ + + + + + 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 000000000..a50be6112 --- /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 000000000..dcb6f8880 --- /dev/null +++ b/layout/style/test/test_grid_shorthand_serialization.html @@ -0,0 +1,236 @@ + + + + + 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 000000000..85edc2a1a --- /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_quirk.html b/layout/style/test/test_hover_quirk.html new file mode 100644 index 000000000..c14b741c3 --- /dev/null +++ b/layout/style/test/test_hover_quirk.html @@ -0,0 +1,82 @@ + + + + + Test for the :active and :hover quirk + + + + + + + + Mozilla Bug 783213 +

    +
    + Span
    +
    + Link +
    +
    
    +
    +
    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 000000000..3f7013cc1
    --- /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 000000000..8d7f32ffa --- /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_inherit_computation.html b/layout/style/test/test_inherit_computation.html new file mode 100644 index 000000000..a4407418c --- /dev/null +++ b/layout/style/test/test_inherit_computation.html @@ -0,0 +1,159 @@ + + + + + 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 000000000..f78c70558 --- /dev/null +++ b/layout/style/test/test_inherit_storage.html @@ -0,0 +1,116 @@ + + + + + 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 000000000..8da53ed45 --- /dev/null +++ b/layout/style/test/test_initial_computation.html @@ -0,0 +1,163 @@ + + + + + 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 000000000..f75c0d68c --- /dev/null +++ b/layout/style/test/test_initial_storage.html @@ -0,0 +1,130 @@ + + + + + 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_keyframes_rules.html b/layout/style/test/test_keyframes_rules.html new file mode 100644 index 000000000..8446f22dd --- /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_load_events_on_stylesheets.html b/layout/style/test/test_load_events_on_stylesheets.html new file mode 100644 index 000000000..c6dedbed1 --- /dev/null +++ b/layout/style/test/test_load_events_on_stylesheets.html @@ -0,0 +1,152 @@ + + + + + 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 000000000..f1265496d --- /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_media_queries.html b/layout/style/test/test_media_queries.html new file mode 100644 index 000000000..1edac15ae --- /dev/null +++ b/layout/style/test/test_media_queries.html @@ -0,0 +1,845 @@ + + + + + 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 000000000..6c9159186 --- /dev/null +++ b/layout/style/test/test_media_queries_dynamic.html @@ -0,0 +1,213 @@ + + + + + Test for Bug 473400 + + + + +Mozilla Bug 473400 + + +
    +
    +
    + + + diff --git a/layout/style/test/test_media_queries_dynamic_xbl.html b/layout/style/test/test_media_queries_dynamic_xbl.html new file mode 100644 index 000000000..f7bbde18e --- /dev/null +++ b/layout/style/test/test_media_queries_dynamic_xbl.html @@ -0,0 +1,40 @@ + + + + + Test for Bug 156716 + + + + +Mozilla Bug 156716 + +
    +
    +
    + + + 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 000000000..b50771bf6 --- /dev/null +++ b/layout/style/test/test_media_query_list.html @@ -0,0 +1,367 @@ + + + + + Test for MediaQueryList (Bug 542058) + + + + +Mozilla Bug 542058 + + +
    +
    +
    + + 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 000000000..d3a8cc27c --- /dev/null +++ b/layout/style/test/test_moz_device_pixel_ratio.html @@ -0,0 +1,77 @@ + + + + + Test for Bug 474356 + + + + + + +Mozilla Bug 474356 + + +
    +
    +
    +
    + +
    + + diff --git a/layout/style/test/test_namespace_rule.html b/layout/style/test/test_namespace_rule.html new file mode 100644 index 000000000..2cf4c4fc5 --- /dev/null +++ b/layout/style/test/test_namespace_rule.html @@ -0,0 +1,462 @@ + + + + Test for CSS Namespace rules + + + + +

    +
    +
    +
    + + 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 000000000..7ab286bde --- /dev/null +++ b/layout/style/test/test_of_type_selectors.xhtml @@ -0,0 +1,98 @@ + + + + Test for *-of-type selectors in Bug 75375 + + + + +Mozilla Bug 75375 + +
    +
    +
    + + + diff --git a/layout/style/test/test_page_parser.html b/layout/style/test/test_page_parser.html new file mode 100644 index 000000000..8c94be0bf --- /dev/null +++ b/layout/style/test/test_page_parser.html @@ -0,0 +1,107 @@ + + + + + + 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 000000000..74737fd5e
    --- /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 000000000..e083aad6c --- /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 000000000..ebaf5aa5d --- /dev/null +++ b/layout/style/test/test_parse_rule.html @@ -0,0 +1,256 @@ + + + + + + + + + +
    + diff --git a/layout/style/test/test_parse_url.html b/layout/style/test/test_parse_url.html new file mode 100644 index 000000000..aa167398f --- /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 000000000..384d4dfa6 --- /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 000000000..37f9ec83f --- /dev/null +++ b/layout/style/test/test_pixel_lengths.html @@ -0,0 +1,75 @@ + + + + Test that pixel lengths don't change based on DPI + + + + +
    + +
    pt
    +
    pc
    +
    mm
    +
    cm
    +
    in
    +
    q
    + +
    mozmm
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_pointer-events.html b/layout/style/test/test_pointer-events.html new file mode 100644 index 000000000..73db14351 --- /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 000000000..03d3eb26b --- /dev/null +++ b/layout/style/test/test_position_float_display.html @@ -0,0 +1,107 @@ + + + + + + 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 000000000..9deb92333 --- /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_priority_preservation.html b/layout/style/test/test_priority_preservation.html new file mode 100644 index 000000000..080a4651c --- /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 000000000..30e6618fc --- /dev/null +++ b/layout/style/test/test_property_database.html @@ -0,0 +1,170 @@ + + + + + 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 000000000..be1127def --- /dev/null +++ b/layout/style/test/test_property_syntax_errors.html @@ -0,0 +1,153 @@ + + + + + Test that we reject syntax errors listed in property_database.js + + + + + +

    + + +
    +
    +
    + + diff --git a/layout/style/test/test_pseudoelement_parsing.html b/layout/style/test/test_pseudoelement_parsing.html new file mode 100644 index 000000000..b6fcf783f --- /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 000000000..ad4bf5242 --- /dev/null +++ b/layout/style/test/test_pseudoelement_state.html @@ -0,0 +1,164 @@ + +Test for Bug 922669 + + + + + + + + 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 000000000..8349d692b --- /dev/null +++ b/layout/style/test/test_redundant_font_download.html @@ -0,0 +1,130 @@ + + + + + + Test for bug 879963 + + + + + + + + + + + Mozilla Bug 879963 + +
    + + +
    + +
    + Test +
    + +
    + +
    + + + + + + diff --git a/layout/style/test/test_rem_unit.html b/layout/style/test/test_rem_unit.html new file mode 100644 index 000000000..8d18f9d50 --- /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_restyles_in_smil_animation.html b/layout/style/test/test_restyles_in_smil_animation.html new file mode 100644 index 000000000..8e18da053 --- /dev/null +++ b/layout/style/test/test_restyles_in_smil_animation.html @@ -0,0 +1,113 @@ + + + +Tests restyles in smil animation + + + + + + + +
    + + + +
    + + + 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 000000000..547fb0e48 --- /dev/null +++ b/layout/style/test/test_root_node_display.html @@ -0,0 +1,67 @@ + + + + + + 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 000000000..a55ec08c5 --- /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_rule_serialization.html b/layout/style/test/test_rule_serialization.html new file mode 100644 index 000000000..eba9a3e6f --- /dev/null +++ b/layout/style/test/test_rule_serialization.html @@ -0,0 +1,53 @@ + + + + + Test for Bug + + + + + +
    +
    +
    + + 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 000000000..ab692239a --- /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 000000000..7294a879f --- /dev/null +++ b/layout/style/test/test_selectors.html @@ -0,0 +1,1297 @@ + + + + Test for CSS Selectors + + + + +

    +
    +
    +
    + + diff --git a/layout/style/test/test_selectors_on_anonymous_content.html b/layout/style/test/test_selectors_on_anonymous_content.html new file mode 100644 index 000000000..a4975f6b8 --- /dev/null +++ b/layout/style/test/test_selectors_on_anonymous_content.html @@ -0,0 +1,78 @@ + + + + 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 000000000..74de52cbe --- /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_shorthand_property_getters.html b/layout/style/test/test_shorthand_property_getters.html new file mode 100644 index 000000000..da4d8de3a --- /dev/null +++ b/layout/style/test/test_shorthand_property_getters.html @@ -0,0 +1,268 @@ + + + + + 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 000000000..edf84fe8d --- /dev/null +++ b/layout/style/test/test_specified_value_serialization.html @@ -0,0 +1,105 @@ + + + + Test for miscellaneous specified value issues + + + + +Mozilla Bug +

    + + +
    +
    +
    + + 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 000000000..d0941042f --- /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 000000000..d49e7ecd9 --- /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 000000000..b020b0844 --- /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_supports_rules.html b/layout/style/test/test_supports_rules.html new file mode 100644 index 000000000..834671375 --- /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 000000000..65b016836 --- /dev/null +++ b/layout/style/test/test_system_font_serialization.html @@ -0,0 +1,59 @@ + + + + + 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 000000000..aff21e1d8 --- /dev/null +++ b/layout/style/test/test_text_decoration_shorthands.html @@ -0,0 +1,93 @@ + + + + + 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 000000000..de7943a49 --- /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 000000000..b4213dbdd --- /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 000000000..68085a712 --- /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 000000000..265339af4 --- /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_bug537151.html b/layout/style/test/test_transitions_bug537151.html new file mode 100644 index 000000000..a3640e393 --- /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 000000000..4fca67ada --- /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 000000000..f0421eeb4 --- /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 000000000..c84b8e6a8 --- /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 000000000..b74c50a49 --- /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 000000000..6df8ab30c --- /dev/null +++ b/layout/style/test/test_transitions_events.html @@ -0,0 +1,282 @@ + + + + + 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 000000000..29e2ae24c --- /dev/null +++ b/layout/style/test/test_transitions_per_property.html @@ -0,0 +1,2565 @@ + + + + + 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 000000000..a9e197d16 --- /dev/null +++ b/layout/style/test/test_transitions_replacement_on_busy_frame.html @@ -0,0 +1,30 @@ + + + + + Test for bug 1167519 + + + + +Mozilla Bug + 1167519 +
    +
    +
    + + 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 000000000..920b48af3 --- /dev/null +++ b/layout/style/test/test_transitions_step_functions.html @@ -0,0 +1,93 @@ + + + + + Test for Bug 435441 + + + + + + +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_transitions_with_disabled_properties.html b/layout/style/test/test_transitions_with_disabled_properties.html new file mode 100644 index 000000000..fc0965f42 --- /dev/null +++ b/layout/style/test/test_transitions_with_disabled_properties.html @@ -0,0 +1,28 @@ + + + + + + Test for bug 1265611 + + + + +Mozilla Bug + 1265611 + +
    +
    +
    + + diff --git a/layout/style/test/test_unclosed_parentheses.html b/layout/style/test/test_unclosed_parentheses.html new file mode 100644 index 000000000..d2daae944 --- /dev/null +++ b/layout/style/test/test_unclosed_parentheses.html @@ -0,0 +1,289 @@ + + + + + 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 000000000..43622e2ae --- /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 000000000..9ad800f13 --- /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 000000000..bb8984726 --- /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 000000000..6db3f0c7d --- /dev/null +++ b/layout/style/test/test_units_length.html @@ -0,0 +1,59 @@ + + + + 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 000000000..e9d3e77bd --- /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_unprefixing_service.html b/layout/style/test/test_unprefixing_service.html new file mode 100644 index 000000000..c489e2ac0 --- /dev/null +++ b/layout/style/test/test_unprefixing_service.html @@ -0,0 +1,93 @@ + + + + + + Test for Bug 1107378 + + + + + +Mozilla Bug 1107378 +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_unprefixing_service_prefs.html b/layout/style/test/test_unprefixing_service_prefs.html new file mode 100644 index 000000000..329dce2a6 --- /dev/null +++ b/layout/style/test/test_unprefixing_service_prefs.html @@ -0,0 +1,132 @@ + + + + + + Test for Bug 1132743 + + + + + +Mozilla Bug 1132743 +
    + +
    +
    +
    +
    + + diff --git a/layout/style/test/test_value_cloning.html b/layout/style/test/test_value_cloning.html new file mode 100644 index 000000000..5582b8303 --- /dev/null +++ b/layout/style/test/test_value_cloning.html @@ -0,0 +1,182 @@ + + + + + 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 000000000..024b26210 --- /dev/null +++ b/layout/style/test/test_value_computation.html @@ -0,0 +1,249 @@ + + + + + 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 000000000..5e7fa6b69 --- /dev/null +++ b/layout/style/test/test_value_storage.html @@ -0,0 +1,352 @@ + + + + + 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 000000000..c93ae8938 --- /dev/null +++ b/layout/style/test/test_variable_serialization_computed.html @@ -0,0 +1,82 @@ + +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 000000000..62321eaaf --- /dev/null +++ b/layout/style/test/test_variable_serialization_specified.html @@ -0,0 +1,117 @@ + +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 000000000..a1dd341a7 --- /dev/null +++ b/layout/style/test/test_variables.html @@ -0,0 +1,125 @@ + +Assorted CSS variable 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 000000000..d19a6750c --- /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_units.html b/layout/style/test/test_viewport_units.html new file mode 100644 index 000000000..d1d35b964 --- /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 000000000..1a18d7ac3 --- /dev/null +++ b/layout/style/test/test_visited_image_loading.html @@ -0,0 +1,67 @@ + + + + + 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 000000000..42b7c7aff --- /dev/null +++ b/layout/style/test/test_visited_image_loading_empty.html @@ -0,0 +1,67 @@ + + + + + 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 000000000..1d5fe8c7f --- /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 000000000..3526b833f --- /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 000000000..af6e63b9f --- /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 000000000..ed655bd8a --- /dev/null +++ b/layout/style/test/test_webkit_device_pixel_ratio.html @@ -0,0 +1,77 @@ + + + + + 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 000000000..b84d16608 --- /dev/null +++ b/layout/style/test/test_webkit_flex_display.html @@ -0,0 +1,48 @@ + + + + + Test for Bug 1274096 + + + + +Mozilla Bug 1274096 + + +
    +
    +
    + + diff --git a/layout/style/test/unprefixing_service_iframe.html b/layout/style/test/unprefixing_service_iframe.html new file mode 100644 index 000000000..8edeb20dc --- /dev/null +++ b/layout/style/test/unprefixing_service_iframe.html @@ -0,0 +1,394 @@ + + + + + Helper file for testing CSS Unprefixing Service + + + + +
    +
    +
    + + + + diff --git a/layout/style/test/unprefixing_service_utils.js b/layout/style/test/unprefixing_service_utils.js new file mode 100644 index 000000000..cd17d20d0 --- /dev/null +++ b/layout/style/test/unprefixing_service_utils.js @@ -0,0 +1,87 @@ +/* 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/. */ + +// Shared data & functionality used in tests for CSS Unprefixing Service. + +// Whitelisted hosts: +// (per implementation of nsPrincipal::IsOnCSSUnprefixingWhitelist()) +var gWhitelistedHosts = [ + // test1.example.org is on the whitelist. + "test1.example.org", + // test2.example.org is on the "allow all subdomains" whitelist. + "test2.example.org", + "sub1.test2.example.org", + "sub2.test2.example.org" +]; + +// *NOT* whitelisted hosts: +var gNotWhitelistedHosts = [ + // Though test1.example.org is on the whitelist, its subdomains are not. + "sub1.test1.example.org", + // mochi.test is not on the whitelist. + "mochi.test:8888" +]; + +// Names of prefs: +const PREF_UNPREFIXING_SERVICE = + "layout.css.unprefixing-service.enabled"; +const PREF_INCLUDE_TEST_DOMAINS = + "layout.css.unprefixing-service.include-test-domains"; + +// Helper-function to make unique URLs in testHost(): +var gCounter = 0; +function getIncreasingCounter() { + return gCounter++; +} + +// This function tests a particular host in our iframe. +// @param aHost The host to be tested +// @param aExpectEnabled Should we expect unprefixing to be enabled for host? +function testHost(aHost, aExpectEnabled) { + // Build the URL: + let url = window.location.protocol; // "http:" or "https:" + url += "//"; + url += aHost; + + // Append the path-name, up to the actual filename (the final "/"): + const re = /(.*\/).*/; + url += window.location.pathname.replace(re, "$1"); + url += IFRAME_TESTFILE; + // In case this is the same URL as last time, we add "?N" for some unique N, + // to make each URL different, so that the iframe actually (re)loads: + url += "?" + getIncreasingCounter(); + // We give the URL a #suffix to indicate to the test whether it should expect + // that unprefixing is enabled or disabled: + url += (aExpectEnabled ? "#expectEnabled" : "#expectDisabled"); + + let iframe = document.getElementById("testIframe"); + iframe.contentWindow.location = url; + // The iframe will report its results back via postMessage. + // Our caller had better have set up a postMessage listener. +} + +// Register a postMessage() handler, to allow our cross-origin iframe to +// communicate back to the main page's mochitest functionality. +// The handler expects postMessage to be called with an object like: +// { type: ["is"|"ok"|"testComplete"], ... } +// The "is" and "ok" types will trigger the corresponding function to be +// called in the main page, with named arguments provided in the payload. +// The "testComplete" type will trigger the passed-in aTestCompleteCallback +// function to be invoked (e.g. to advance to the next testcase, or to finish +// the overall test, as-appropriate). +function registerPostMessageListener(aTestCompleteCallback) { + let receiveMessage = function(event) { + if (event.data.type === "is") { + is(event.data.actual, event.data.expected, event.data.desc); + } else if (event.data.type === "ok") { + ok(event.data.condition, event.data.desc); + } else if (event.data.type === "testComplete") { + aTestCompleteCallback(); + } else { + ok(false, "unrecognized data in postMessage call"); + } + }; + + window.addEventListener("message", receiveMessage, false); +} diff --git a/layout/style/test/unstyled-frame.css b/layout/style/test/unstyled-frame.css new file mode 100644 index 000000000..e69de29bb diff --git a/layout/style/test/unstyled-frame.xml b/layout/style/test/unstyled-frame.xml new file mode 100644 index 000000000..833b4f112 --- /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 000000000..82767f9b2 --- /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 000000000..86b7c54ac --- /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 000000000..fd71a3cd3 --- /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 000000000..ad1dac758 --- /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 000000000..31da176e4 --- /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 000000000..880305439 --- /dev/null +++ b/layout/style/test/visited_image_loading.sjs @@ -0,0 +1,60 @@ +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 000000000..f919f5eb7 --- /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 000000000..21579bb9c --- /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/test/xbl_bindings.xml b/layout/style/test/xbl_bindings.xml new file mode 100644 index 000000000..90d68ae06 --- /dev/null +++ b/layout/style/test/xbl_bindings.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/layout/style/test/xpcshell.ini b/layout/style/test/xpcshell.ini new file mode 100644 index 000000000..5019e0a48 --- /dev/null +++ b/layout/style/test/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +tail = + +[test_csslexer.js] diff --git a/layout/style/xbl-marquee/jar.mn b/layout/style/xbl-marquee/jar.mn new file mode 100644 index 000000000..9247cb4a1 --- /dev/null +++ b/layout/style/xbl-marquee/jar.mn @@ -0,0 +1,8 @@ +# 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: +% content xbl-marquee %content/xbl-marquee/ contentaccessible=yes + content/xbl-marquee/xbl-marquee.xml + content/xbl-marquee/xbl-marquee.css diff --git a/layout/style/xbl-marquee/moz.build b/layout/style/xbl-marquee/moz.build new file mode 100644 index 000000000..eb4454d28 --- /dev/null +++ b/layout/style/xbl-marquee/moz.build @@ -0,0 +1,7 @@ +# -*- 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/. + +JAR_MANIFESTS += ['jar.mn'] \ No newline at end of file diff --git a/layout/style/xbl-marquee/xbl-marquee.css b/layout/style/xbl-marquee/xbl-marquee.css new file mode 100644 index 000000000..e6d3ee94b --- /dev/null +++ b/layout/style/xbl-marquee/xbl-marquee.css @@ -0,0 +1,12 @@ +/* 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/. */ + +/* PRINT ONLY rules */ +@media print { + + marquee > * > * { + margin: 0 !important; + padding: 0 !important; + } /* This hack is needed until bug 119078 gets fixed */ +} diff --git a/layout/style/xbl-marquee/xbl-marquee.xml b/layout/style/xbl-marquee/xbl-marquee.xml new file mode 100644 index 000000000..bb837624d --- /dev/null +++ b/layout/style/xbl-marquee/xbl-marquee.xml @@ -0,0 +1,733 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + var val = parseInt(val); + if (val > 0 ) { + this.setAttribute("scrolldelay", val); + } + + + + + + + + + + + + + + + this._mutationActor(this._mutationObserver.takeRecords()); + return this._direction; + + + + + + + + + this._mutationActor(this._mutationObserver.takeRecords()); + return this._behavior; + + + if (typeof val == 'string') { + val = val.toLowerCase(); + } + if (val == "alternate" || val == "slide" || val == 'scroll') { + this.setAttribute("behavior", val); + } + + + + + + + + + + 0) { + this.setAttribute("loop", val); + } + ]]> + + + + + + + return this.getAttribute("onstart"); + + + this._setEventListener("start", val, true); + this.setAttribute("onstart", val); + + + + + + return this.getAttribute("onfinish"); + + + this._setEventListener("finish", val, true); + this.setAttribute("onfinish", val); + + + + + + return this.getAttribute("onbounce"); + + + this._setEventListener("bounce", val, true); + this.setAttribute("onbounce", val); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this.outerDiv.offsetHeight) { + corrvalue = this.originalHeight - this.outerDiv.offsetHeight; + } + this.innerDiv.style.padding = height + " 0"; + this.dirsign = 1; + this.startAt = (this._behavior == 'alternate') ? (this.originalHeight - corrvalue) : 0; + this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? + (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height)); + break; + + case "down": + var height = document.defaultView.getComputedStyle(this, "").height; + this.outerDiv.style.height = height; + if (this.originalHeight > this.outerDiv.offsetHeight) { + corrvalue = this.originalHeight - this.outerDiv.offsetHeight; + } + this.innerDiv.style.padding = height + " 0"; + this.dirsign = -1; + this.startAt = (this._behavior == 'alternate') ? + (parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height)); + this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? + (this.originalHeight - corrvalue) : 0; + break; + + case "right": + if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) { + corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth; + } + this.dirsign = -1; + this.stopAt = (this._behavior == 'alternate' || this._behavior == 'slide') ? + (this.innerDiv.offsetWidth - corrvalue) : 0; + this.startAt = this.outerDiv.offsetWidth + ((this._behavior == 'alternate') ? + corrvalue : (this.innerDiv.offsetWidth + this.stopAt)); + break; + + case "left": + default: + if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) { + corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth; + } + this.dirsign = 1; + this.startAt = (this._behavior == 'alternate') ? (this.innerDiv.offsetWidth - corrvalue) : 0; + this.stopAt = this.outerDiv.offsetWidth + + ((this._behavior == 'alternate' || this._behavior == 'slide') ? + corrvalue : (this.innerDiv.offsetWidth + this.startAt)); + } + + if (aResetPosition) { + this.newPosition = this.startAt; + this._fireEvent("start", false, false); + } + } //end if + + this.newPosition = this.newPosition + (this.dirsign * this._scrollAmount); + + if ((this.dirsign == 1 && this.newPosition > this.stopAt) || + (this.dirsign == -1 && this.newPosition < this.stopAt)) + { + switch (this._behavior) + { + case 'alternate': + // lets start afresh + this.startNewDirection = true; + + // swap direction + const swap = {left: "right", down: "up", up: "down", right: "left"}; + this._direction = swap[this._direction]; + this.newPosition = this.stopAt; + + if ((this._direction == "up") || (this._direction == "down")) { + this.outerDiv.scrollTop = this.newPosition; + } else { + this.outerDiv.scrollLeft = this.newPosition; + } + + if (this._loop != 1) { + this._fireEvent("bounce", false, true); + } + break; + + case 'slide': + if (this._loop > 1) { + this.newPosition = this.startAt; + } + break; + + default: + this.newPosition = this.startAt; + + if ((this._direction == "up") || (this._direction == "down")) { + this.outerDiv.scrollTop = this.newPosition; + } else { + this.outerDiv.scrollLeft = this.newPosition; + } + + //dispatch start event, even when this._loop == 1, comp. with IE6 + this._fireEvent("start", false, false); + } + + if (this._loop > 1) { + this._loop--; + } else if (this._loop == 1) { + if ((this._direction == "up") || (this._direction == "down")) { + this.outerDiv.scrollTop = this.stopAt; + } else { + this.outerDiv.scrollLeft = this.stopAt; + } + this.stop(); + this._fireEvent("finish", false, true); + return; + } + } + else { + if ((this._direction == "up") || (this._direction == "down")) { + this.outerDiv.scrollTop = this.newPosition; + } else { + this.outerDiv.scrollLeft = this.newPosition; + } + } + + var myThis = this; + var lambda = function myTimeOutFunction(){myThis._doMove(false);} + this.runId = window.setTimeout(lambda, this._scrollDelay); + ]]> + + + + + + + + + + + + + 0) { + var mutation = aMutations.shift(); + var attrName = mutation.attributeName.toLowerCase(); + var oldValue = mutation.oldValue; + var target = mutation.target; + var newValue = target.getAttribute(attrName); + + if (oldValue != newValue) { + switch (attrName) { + case "loop": + target._set_loop(newValue); + if (target.rundId == 0) { + target.start(); + } + break; + case "scrollamount": + target._set_scrollAmount(newValue); + break; + case "scrolldelay": + target._set_scrollDelay(newValue); + target.stop(); + target.start(); + break; + case "truespeed": + //needed to update target._scrollDelay + var myThis = target; + var lambda = function() {myThis._set_scrollDelay(myThis.getAttribute('scrolldelay'));} + window.setTimeout(lambda, 0); + break; + case "behavior": + target._set_behavior(newValue); + target.startNewDirection = true; + if ((oldValue == "slide" && target.newPosition == target.stopAt) || + newValue == "alternate" || newValue == "slide") { + target.stop(); + target._doMove(true); + } + break; + case "direction": + if (!newValue) { + newValue = "left"; + } + target._set_direction(newValue); + break; + case "width": + case "height": + target.startNewDirection = true; + break; + case "onstart": + target._setEventListener("start", newValue); + break; + case "onfinish": + target._setEventListener("finish", newValue); + break; + case "onbounce": + target._setEventListener("bounce", newValue); + break; + } + } + } + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3