diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 5a90a29123e..49aac1368bd 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -83,7 +83,9 @@ public boolean isValidURI(String URI) } - //Function to merge two arrays into third array. + //Function to merge two arrays into third array. + // Unused and deprecated + @Deprecated public static T[] concat(T[] a, T[] b) { final int alen = a.length; final int blen = b.length; @@ -113,10 +115,14 @@ public static T[] concat(T[] a, T[] b) { private final static HashSet allelementVerifiers=new HashSet(); //Reference http://www.w3.org/TR/CSS2/propidx.html static { + allelementVerifiers.add("accent-color"); allelementVerifiers.add("align-content"); allelementVerifiers.add("align-items"); allelementVerifiers.add("align-self"); + allelementVerifiers.add("all"); + allelementVerifiers.add("appearance"); allelementVerifiers.add("azimuth"); + allelementVerifiers.add("backface-visibility"); allelementVerifiers.add("background-attachment"); allelementVerifiers.add("background-blend-mode"); allelementVerifiers.add("background-clip"); @@ -124,6 +130,8 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("background-image"); allelementVerifiers.add("background-origin"); allelementVerifiers.add("background-position"); + allelementVerifiers.add("background-position-x"); + allelementVerifiers.add("background-position-y"); allelementVerifiers.add("background-repeat"); allelementVerifiers.add("background-size"); allelementVerifiers.add("background"); @@ -152,6 +160,10 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("border-top"); allelementVerifiers.add("border-right"); allelementVerifiers.add("border-bottom"); + allelementVerifiers.add("border-block-end"); + allelementVerifiers.add("border-block-start"); + allelementVerifiers.add("border-inline-end"); + allelementVerifiers.add("border-inline-start"); allelementVerifiers.add("border-top-width"); allelementVerifiers.add("border-right-width"); allelementVerifiers.add("border-bottom-width"); @@ -229,6 +241,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("float"); allelementVerifiers.add("font-family"); allelementVerifiers.add("font-kerning"); + allelementVerifiers.add("font-optical-sizing"); allelementVerifiers.add("font-size"); allelementVerifiers.add("font-style"); allelementVerifiers.add("font-variant"); @@ -236,7 +249,12 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("font"); allelementVerifiers.add("hanging-punctuation"); allelementVerifiers.add("height"); + allelementVerifiers.add("hyphenate-character"); + allelementVerifiers.add("hyphens"); + allelementVerifiers.add("image-orientation"); + allelementVerifiers.add("image-rendering"); allelementVerifiers.add("inline-size"); + allelementVerifiers.add("isolation"); allelementVerifiers.add("justify-content"); allelementVerifiers.add("justify-items"); allelementVerifiers.add("justify-self"); @@ -274,6 +292,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("nav-right"); allelementVerifiers.add("nav-up"); allelementVerifiers.add("object-fit"); + allelementVerifiers.add("object-position"); allelementVerifiers.add("opacity"); allelementVerifiers.add("order"); allelementVerifiers.add("orphans"); @@ -283,7 +302,16 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("outline-width"); allelementVerifiers.add("outline"); allelementVerifiers.add("overflow"); + allelementVerifiers.add("overflow-block"); + allelementVerifiers.add("overflow-inline"); allelementVerifiers.add("overflow-wrap"); + allelementVerifiers.add("overflow-x"); + allelementVerifiers.add("overflow-y"); + allelementVerifiers.add("overscroll-behavior"); + allelementVerifiers.add("overscroll-behavior-block"); + allelementVerifiers.add("overscroll-behavior-inline"); + allelementVerifiers.add("overscroll-behavior-x"); + allelementVerifiers.add("overscroll-behavior-y"); allelementVerifiers.add("padding-block"); allelementVerifiers.add("padding-block-end"); allelementVerifiers.add("padding-block-start"); @@ -301,16 +329,44 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("pause-after"); allelementVerifiers.add("pause-before"); allelementVerifiers.add("pause"); + allelementVerifiers.add("perspective"); allelementVerifiers.add("pitch-range"); allelementVerifiers.add("pitch"); allelementVerifiers.add("play-during"); allelementVerifiers.add("punctuation-trim"); + allelementVerifiers.add("pointer-events"); allelementVerifiers.add("position"); allelementVerifiers.add("quotes"); allelementVerifiers.add("resize"); allelementVerifiers.add("richness"); allelementVerifiers.add("right"); + allelementVerifiers.add("rotate"); + allelementVerifiers.add("row-gap"); + allelementVerifiers.add("ruby-align"); + allelementVerifiers.add("ruby-position"); allelementVerifiers.add("scroll-behavior"); + allelementVerifiers.add("scroll-margin"); + allelementVerifiers.add("scroll-margin-block"); + allelementVerifiers.add("scroll-margin-block-end"); + allelementVerifiers.add("scroll-margin-block-start"); + allelementVerifiers.add("scroll-margin-bottom"); + allelementVerifiers.add("scroll-margin-inline"); + allelementVerifiers.add("scroll-margin-inline-end"); + allelementVerifiers.add("scroll-margin-inline-start"); + allelementVerifiers.add("scroll-margin-left"); + allelementVerifiers.add("scroll-margin-right"); + allelementVerifiers.add("scroll-margin-top"); + allelementVerifiers.add("scroll-padding"); + allelementVerifiers.add("scroll-padding-block"); + allelementVerifiers.add("scroll-padding-block-end"); + allelementVerifiers.add("scroll-padding-block-start"); + allelementVerifiers.add("scroll-padding-bottom"); + allelementVerifiers.add("scroll-padding-inline"); + allelementVerifiers.add("scroll-padding-inline-end"); + allelementVerifiers.add("scroll-padding-inline-start"); + allelementVerifiers.add("scroll-padding-left"); + allelementVerifiers.add("scroll-padding-right"); + allelementVerifiers.add("scroll-padding-top"); allelementVerifiers.add("scroll-snap-align"); allelementVerifiers.add("scroll-snap-stop"); allelementVerifiers.add("scroll-snap-type"); @@ -330,6 +386,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("text-decoration-color"); allelementVerifiers.add("text-decoration-line"); allelementVerifiers.add("text-decoration-skip"); + allelementVerifiers.add("text-decoration-skip-ink"); allelementVerifiers.add("text-decoration-style"); allelementVerifiers.add("text-decoration-thickness"); allelementVerifiers.add("text-emphasis"); @@ -346,6 +403,8 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("text-underline-offset"); allelementVerifiers.add("text-underline-position"); allelementVerifiers.add("text-wrap"); + allelementVerifiers.add("text-wrap-mode"); + allelementVerifiers.add("text-wrap-style"); allelementVerifiers.add("top"); allelementVerifiers.add("transform"); allelementVerifiers.add("transform-origin"); @@ -353,6 +412,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("transition-duration"); allelementVerifiers.add("transition-property"); allelementVerifiers.add("transition-timing-function"); + allelementVerifiers.add("translate"); allelementVerifiers.add("unicode-bidi"); allelementVerifiers.add("vertical-align"); allelementVerifiers.add("visibility"); @@ -379,8 +439,8 @@ public static T[] concat(T[] a, T[] b) { { /*CSSPropertyVerifier(String[] allowedValues,String[] possibleValues,String expression,boolean onlyValueVerifier)*/ //for background-position - auxilaryVerifiers[2]=new CSSPropertyVerifier(Arrays.asList("left","center","right"),Arrays.asList("pe","le"),null,null,true); - auxilaryVerifiers[3]=new CSSPropertyVerifier(Arrays.asList("top","center","bottom"),Arrays.asList("pe","le"),null,null,true); + auxilaryVerifiers[2]=new CSSPropertyVerifier(Arrays.asList("left","center","right"),Arrays.asList("pe","le"),null,null,true); // FIXME: Side-relative values with 2 tokens + auxilaryVerifiers[3]=new CSSPropertyVerifier(Arrays.asList("top","center","bottom"),Arrays.asList("pe","le"),null,null,true); // FIXME: Side-relative values with 2 tokens auxilaryVerifiers[4]=new CSSPropertyVerifier(Arrays.asList("left","center","right"),null,null,null,true); auxilaryVerifiers[5]=new CSSPropertyVerifier(Arrays.asList("top","center","bottom"),null,null,null,true); // @@ -479,7 +539,10 @@ public static T[] concat(T[] a, T[] b) { */ private static void addVerifier(String element) { - if ("align-content".equalsIgnoreCase(element)) { + if("accent-color".equalsIgnoreCase(element)){ + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("co"))); + allelementVerifiers.remove(element); + } else if ("align-content".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA, null,Arrays.asList("121a124"), true,true)); allelementVerifiers.remove(element); } else if("align-items".equalsIgnoreCase(element)) { @@ -488,11 +551,20 @@ private static void addVerifier(String element) } else if("align-self".equalsIgnoreCase(element)) { elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto", "stretch", "baseline", "last-baseline"), ElementInfo.VISUALMEDIA, null, Arrays.asList("127"), true, true)); allelementVerifiers.remove(element); + } else if("all".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(null, ElementInfo.MEDIA, null, null, true, true)); + allelementVerifiers.remove(element); + } else if("appearance".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto", "none", "base", "searchfield", "textarea", "checkbox", "radio", "menulist", "listbox", "meter", "progress-bar", "button", "textfield", "menulist-button"), ElementInfo.VISUALMEDIA, null, null, true, true)); + allelementVerifiers.remove(element); } else if("azimuth".equalsIgnoreCase(element)) { auxilaryVerifiers[0]=new CSSPropertyVerifier(Arrays.asList("left-side","far-left","left","center-left","center","center-right","right","far-right","right-side"),null,null,null,true); auxilaryVerifiers[1]=new CSSPropertyVerifier(Arrays.asList("behind"),null,null,null,true); elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("leftwards","rightwards"),ElementInfo.AURALMEDIA,Arrays.asList("an"),Arrays.asList("0a1"))); allelementVerifiers.remove(element); + } else if("backface-visibility".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("visible", "hidden"), ElementInfo.VISUALMEDIA, null, null, true, true)); + allelementVerifiers.remove(element); } else if("background-attachment".equalsIgnoreCase(element)){ auxilaryVerifiers[60] = new CSSPropertyVerifier(Arrays.asList("local","scroll","fixed"), null, null, null, true); @@ -508,12 +580,10 @@ else if("background-clip".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("61<1,65535>"), true,true)); allelementVerifiers.remove(element); - } else if("background-color".equalsIgnoreCase(element)){ elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("transparent"),ElementInfo.VISUALMEDIA,Arrays.asList("co"))); allelementVerifiers.remove(element); - } else if("background-image".equalsIgnoreCase(element)){ auxilaryVerifiers[56] = new CSSPropertyVerifier(Arrays.asList("none"),Arrays.asList("ur"),null,null,true); @@ -526,11 +596,6 @@ else if("background-origin".equalsIgnoreCase(element)) allelementVerifiers.remove(element); } - else if("background-position".equalsIgnoreCase(element)) - { // FIXME: css3 http://www.w3.org/TR/css3-background/#background-position - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("2 3?","4a5"))); - allelementVerifiers.remove(element); - } else if("background-repeat".equalsIgnoreCase(element)) { auxilaryVerifiers[57] = new CSSPropertyVerifier(Arrays.asList("repeat","space","round","no-repeat"),null,null,null,true); @@ -541,9 +606,8 @@ else if("background-repeat".equalsIgnoreCase(element)) } else if("background-size".equalsIgnoreCase(element)) { - auxilaryVerifiers[61] = new CSSPropertyVerifier(Arrays.asList("auto"),Arrays.asList("le", "pe"),null,null,true); auxilaryVerifiers[62] = new CSSPropertyVerifier(Arrays.asList("cover", "contain"), null, null, null, true); - auxilaryVerifiers[63] = new CSSPropertyVerifier(null, null, Arrays.asList("61<1,2>", "62"), null, true); + auxilaryVerifiers[63] = new CSSPropertyVerifier(null, null, Arrays.asList("36<1,2>", "62"), null, true); elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("63<1,65535>"), true,true)); allelementVerifiers.remove(element); } @@ -595,7 +659,8 @@ else if("border-top-style".equalsIgnoreCase(element) || "border-bottom-style".eq elementVerifiers.put(element,new CSSPropertyVerifier(null, null, Arrays.asList("13"), ElementInfo.VISUALMEDIA, true)); allelementVerifiers.remove(element); } - else if("border-left".equalsIgnoreCase(element) || "border-right".equalsIgnoreCase(element) || "border-top".equalsIgnoreCase(element) || "border-bottom".equalsIgnoreCase(element)) + else if("border-left".equalsIgnoreCase(element) || "border-right".equalsIgnoreCase(element) || "border-top".equalsIgnoreCase(element) || "border-bottom".equalsIgnoreCase(element) + || "border-block-end".equalsIgnoreCase(element) || "border-block-start".equalsIgnoreCase(element) || "border-inline-end".equalsIgnoreCase(element) || "border-inline-start".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("13a14a11"))); allelementVerifiers.remove(element); @@ -710,17 +775,17 @@ else if("clip".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("sh"))); allelementVerifiers.remove(element); } - else if("break-after".equalsIgnoreCase(element)) + else if("break-after".equalsIgnoreCase(element) || "page-break-after".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","always","avoid","left","right", "page", "column", "avoid-page", "avoid-column" ),ElementInfo.VISUALPAGEDMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto", "always", "avoid", "all", "left", "right", "recto", "verso", "page", "column", "avoid-page", "avoid-column"),ElementInfo.VISUALPAGEDMEDIA)); allelementVerifiers.remove(element); } - else if("break-before".equalsIgnoreCase(element)) + else if("break-before".equalsIgnoreCase(element) || "page-break-before".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","always","avoid","left","right", "page", "column", "avoid-page", "avoid-column" ),ElementInfo.VISUALPAGEDMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto", "always", "avoid", "all", "left", "right", "recto", "verso", "page", "column", "avoid-page", "avoid-column"),ElementInfo.VISUALPAGEDMEDIA)); allelementVerifiers.remove(element); } - else if("break-inside".equalsIgnoreCase(element)) + else if("break-inside".equalsIgnoreCase(element) || "page-break-inside".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","avoid","avoid-page", "avoid-column"),ElementInfo.VISUALPAGEDMEDIA)); allelementVerifiers.remove(element); @@ -965,6 +1030,11 @@ else if("font-kerning".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","none","normal"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("font-optical-sizing".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","none"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("font-size".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("xx-small","x-small","small","medium","large","x-large","xx-large","xxx-large","larger","smaller"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); @@ -996,15 +1066,7 @@ else if("font".equalsIgnoreCase(element)) auxilaryVerifiers[29]=new CSSPropertyVerifier(Arrays.asList("normal","bold","bolder","lighter","100","200","300","400","500","600","700","800","900"),null,null,null,true); //30-32 auxilaryVerifiers[30]=new CSSPropertyVerifier(null,null,Arrays.asList("27a28a29"),null,true); - /* //font-size - auxilaryVerifiers[31]=new CSSPropertyVerifier(Arrays.asList("xx-small","x-small","small","medium","large","x-large","xx-large","larger","smaller"),null,null,true); - //line-height - auxilaryVerifiers[32]=new CSSPropertyVerifier(Arrays.asList("normal"),Arrays.asList("le","pe","re","in"),null,true); - - auxilaryVerifiers[55]=new CSSPropertyVerifier(Arrays.asList("/"),null,null,true); - auxilaryVerifiers[56]=new CSSPropertyVerifier(null,null,Arrays.asList("55 32"),true); - */ auxilaryVerifiers[31]=new FontPartPropertyVerifier(); //font-family auxilaryVerifiers[59]=new FontPropertyVerifier(true); @@ -1016,7 +1078,11 @@ else if("font".equalsIgnoreCase(element)) auxilaryVerifiers[54]=new CSSPropertyVerifier(null,null,Arrays.asList("53 53<0,"+ElementInfo.UPPERLIMIT+">"),true); */ elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("caption","icon","menu","message-box","small-caption","status-bar"),ElementInfo.VISUALMEDIA,null,Arrays.asList("30<0,1>[1,3] 31<0,1>[1,3] 59"),false,true)); - //elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("caption","icon","menu","message-box","small-caption","status-bar"),ElementInfo.VISUALMEDIA,null,Arrays.asList("31<1,1>[1,3]"))); + allelementVerifiers.remove(element); + } + else if("gap".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("85<1,2>"))); allelementVerifiers.remove(element); } else if("hanging-punctuation".equalsIgnoreCase(element)) @@ -1032,11 +1098,38 @@ else if("height".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); } + else if("hyphenate-character".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("st"))); + allelementVerifiers.remove(element); + } + else if("hyphens".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none","auto","manual"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if("image-orientation".equalsIgnoreCase(element)) + { + // image-orientation is currently useless in Freenet because EXIF is stripped. However, "from-image" is the browser default so can't be removed by the filter. Therefore it is kept. + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none","from-image"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if("image-rendering".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","smooth","crisp-edges","pixelated"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("inline-size".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); - } else if ("justify-content".equalsIgnoreCase(element)) { + } + else if("isolation".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","isolate"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if ("justify-content".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("121a124"), true,true)); allelementVerifiers.remove(element); } else if ("justify-items".equalsIgnoreCase(element)) { @@ -1090,58 +1183,23 @@ else if("list-style".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("33a34a35"))); allelementVerifiers.remove(element); } - else if("margin-right".equalsIgnoreCase(element)) + else if("margin-right".equalsIgnoreCase(element) || "margin-left".equalsIgnoreCase(element) || "margin-top".equalsIgnoreCase(element) || "margin-bottom".equalsIgnoreCase(element) + || "margin-block-end".equalsIgnoreCase(element) || "margin-block-start".equalsIgnoreCase(element) || "margin-inline-end".equalsIgnoreCase(element) || "margin-inline-start".equalsIgnoreCase(element) + || "scroll-padding-right".equalsIgnoreCase(element) || "scroll-padding-left".equalsIgnoreCase(element) || "scroll-padding-top".equalsIgnoreCase(element) || "scroll-padding-bottom".equalsIgnoreCase(element) + || "scroll-padding-block-end".equalsIgnoreCase(element) || "scroll-padding-block-start".equalsIgnoreCase(element) || "scroll-padding-inline-end".equalsIgnoreCase(element) || "scroll-padding-inline-start".equalsIgnoreCase(element)) { //margin-width=Length|Percentage|Auto + // scroll-padding-* can have value auto, so it is the same as margin-*; scroll-margin-* can't have value auto, so it is the same as padding-* elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); allelementVerifiers.remove(element); } - else if("margin-left".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-top".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-bottom".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-block".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36<1,2>"))); - allelementVerifiers.remove(element); - } - else if("margin-block-end".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-block-start".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-inline".equalsIgnoreCase(element)) + else if("margin-block".equalsIgnoreCase(element) || "margin-inline".equalsIgnoreCase(element) + || "scroll-padding-block".equalsIgnoreCase(element) || "scroll-padding-inline".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36<1,2>"))); allelementVerifiers.remove(element); } - else if("margin-inline-end".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin-inline-start".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36"))); - allelementVerifiers.remove(element); - } - else if("margin".equalsIgnoreCase(element)) + else if("margin".equalsIgnoreCase(element) || "scroll-padding".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36<1,4>"))); allelementVerifiers.remove(element); @@ -1151,42 +1209,12 @@ else if("math-style".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal","compact"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } - else if("max-block-size".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("max-height".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("max-inline-size".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("max-width".equalsIgnoreCase(element)) + else if("max-width".equalsIgnoreCase(element) || "max-height".equalsIgnoreCase(element) || "max-block-size".equalsIgnoreCase(element) || "max-inline-size".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); } - else if("min-block-size".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("min-height".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("min-inline-size".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); - allelementVerifiers.remove(element); - } - else if("min-width".equalsIgnoreCase(element)) + else if("min-width".equalsIgnoreCase(element) || "min-height".equalsIgnoreCase(element) || "min-block-size".equalsIgnoreCase(element) || "min-inline-size".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); @@ -1212,6 +1240,16 @@ else if ("nav-down".equalsIgnoreCase(element)) { elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("contain","cover","fill","none","scale-down"), ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("background-position".equalsIgnoreCase(element) || "object-position".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(null, ElementInfo.VISUALPAGEDMEDIA,null,Arrays.asList("4a5a40","40 40","4 5","5 4","4 40 5 40","5 40 4 40"))); + allelementVerifiers.remove(element); + } else if("background-position-x".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(null, ElementInfo.VISUALPAGEDMEDIA,null,Arrays.asList("2"))); + allelementVerifiers.remove(element); + } else if("background-position-y".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(null, ElementInfo.VISUALPAGEDMEDIA,null,Arrays.asList("3"))); + allelementVerifiers.remove(element); + } else if("opacity".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALPAGEDMEDIA,Arrays.asList("re"))); @@ -1227,7 +1265,7 @@ else if("orphans".equalsIgnoreCase(element)) } else if("outline-color".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("invert"),ElementInfo.VISUALINTERACTIVEMEDIA,Arrays.asList("co"))); + elementVerifiers.put(element,new CSSPropertyVerifier(null, ElementInfo.VISUALINTERACTIVEMEDIA,Arrays.asList("co"))); allelementVerifiers.remove(element); } else if ("outline-offset".equalsIgnoreCase(element)) { elementVerifiers.put(element, new CSSPropertyVerifier(null, ElementInfo.VISUALMEDIA, Arrays.asList("le"))); @@ -1256,80 +1294,44 @@ else if("outline".equalsIgnoreCase(element)) } else if("overflow".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("visible","hidden","scroll","auto","clip"),ElementInfo.VISUALMEDIA)); - allelementVerifiers.remove(element); - } - else if("padding-top".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); - allelementVerifiers.remove(element); - + auxilaryVerifiers[32]=new CSSPropertyVerifier(Arrays.asList("visible","hidden","scroll","auto","clip"),null,null,null,true); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("32<1,2>"))); } - else if("padding-right".equalsIgnoreCase(element)) + else if("overflow-x".equalsIgnoreCase(element) || "overflow-y".equalsIgnoreCase(element) || "overflow-block".equalsIgnoreCase(element) || "overflow-inline".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); - allelementVerifiers.remove(element); - } - else if("padding-bottom".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); - allelementVerifiers.remove(element); - } - else if("padding-left".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("visible","hidden","scroll","auto","clip"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } - else if("padding-block".equalsIgnoreCase(element)) + else if("overscroll-behavior".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40<1,2>"))); - allelementVerifiers.remove(element); + auxilaryVerifiers[64]=new CSSPropertyVerifier(Arrays.asList("auto","contain","none"),null,null,null,true); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("64<1,2>"))); } - else if("padding-block-end".equalsIgnoreCase(element)) + else if("overscroll-behavior-x".equalsIgnoreCase(element) || "overscroll-behavior-y".equalsIgnoreCase(element) || "overscroll-behavior-block".equalsIgnoreCase(element) || "overscroll-behavior-inline".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","contain","none"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } - else if("padding-block-start".equalsIgnoreCase(element)) + else if("padding-top".equalsIgnoreCase(element) || "padding-right".equalsIgnoreCase(element) || "padding-bottom".equalsIgnoreCase(element) || "padding-left".equalsIgnoreCase(element) + || "padding-block-end".equalsIgnoreCase(element) || "padding-block-start".equalsIgnoreCase(element) || "padding-inline-end".equalsIgnoreCase(element) || "padding-inline-start".equalsIgnoreCase(element) + || "scroll-margin-top".equalsIgnoreCase(element) || "scroll-margin-right".equalsIgnoreCase(element) || "scroll-margin-bottom".equalsIgnoreCase(element) || "scroll-margin-left".equalsIgnoreCase(element) + || "scroll-margin-block-end".equalsIgnoreCase(element) || "scroll-margin-block-start".equalsIgnoreCase(element) || "scroll-margin-inline-end".equalsIgnoreCase(element) || "scroll-margin-inline-start".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); allelementVerifiers.remove(element); } - else if("padding-inline".equalsIgnoreCase(element)) + else if("padding-block".equalsIgnoreCase(element) || "padding-inline".equalsIgnoreCase(element) + || "scroll-margin-block".equalsIgnoreCase(element) || "scroll-margin-inline".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40<1,2>"))); allelementVerifiers.remove(element); } - else if("padding-inline-end".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); - allelementVerifiers.remove(element); - } - else if("padding-inline-start".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40"))); - allelementVerifiers.remove(element); - } - else if("padding".equalsIgnoreCase(element)) + else if("padding".equalsIgnoreCase(element) || "scroll-margin".equalsIgnoreCase(element)) { + // scroll-padding-* can have value auto, so it is the same as margin-*; scroll-margin-* can't have value auto, so it is the same as padding-* elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("40<1,4>"))); allelementVerifiers.remove(element); } - else if("page-break-after".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","always","avoid","left","right"),ElementInfo.VISUALPAGEDMEDIA)); - allelementVerifiers.remove(element); - } - else if("page-break-before".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","always","avoid","left","right"),ElementInfo.VISUALPAGEDMEDIA)); - allelementVerifiers.remove(element); - } - else if("page-break-inside".equalsIgnoreCase(element)) - { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","avoid"),ElementInfo.VISUALPAGEDMEDIA)); - allelementVerifiers.remove(element); - } else if("pause-after".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.AURALMEDIA,Arrays.asList("ti","pe"))); @@ -1345,7 +1347,11 @@ else if("pause".equalsIgnoreCase(element)) auxilaryVerifiers[41]=new CSSPropertyVerifier(null,Arrays.asList("ti","pe"),null,null,true); elementVerifiers.put(element,new CSSPropertyVerifier(null,null,null,Arrays.asList("41<1,2>"))); allelementVerifiers.remove(element); - + } + else if("perspective".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),null,Arrays.asList("le"))); + allelementVerifiers.remove(element); } else if("pitch-range".equalsIgnoreCase(element)) { @@ -1385,6 +1391,11 @@ else if("position".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("static","relative","absolute","fixed","sticky"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("pointer-events".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","none"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("quotes".equalsIgnoreCase(element)) { auxilaryVerifiers[46]=new CSSPropertyVerifier(null,Arrays.asList("st"),null,null,true); @@ -1405,6 +1416,29 @@ else if("right".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); } + else if("rotate".equalsIgnoreCase(element)) + { + auxilaryVerifiers[141] = new CSSPropertyVerifier(null, Arrays.asList("in", "re"), null, null, true); + auxilaryVerifiers[73]=new CSSPropertyVerifier(null,Arrays.asList("an"),null,null,true); + auxilaryVerifiers[15]=new CSSPropertyVerifier(Arrays.asList("x","y","z"),null,null,null,true); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,null,Arrays.asList("73","15 73","141 141 141 73"))); + allelementVerifiers.remove(element); + } + else if("ruby-align".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("start","center","space-between","space-around"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if("ruby-position".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("over","under"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if("row-gap".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); + allelementVerifiers.remove(element); + } else if("scroll-behavior".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","smooth"),ElementInfo.VISUALMEDIA)); @@ -1515,6 +1549,7 @@ else if("text-decoration-line".equalsIgnoreCase(element)) } else if("text-decoration-skip".equalsIgnoreCase(element)) { + // FIXME: the specified values here are invalid auxilaryVerifiers[48]=new CSSPropertyVerifier(Arrays.asList("images"),null,null,null,true); auxilaryVerifiers[49]=new CSSPropertyVerifier(Arrays.asList("spaces"),null,null,null,true); auxilaryVerifiers[50]=new CSSPropertyVerifier(Arrays.asList("ink"),null,null,null,true); @@ -1523,6 +1558,11 @@ else if("text-decoration-skip".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,null,Arrays.asList("48a49a50a51"))); allelementVerifiers.remove(element); } + else if("text-decoration-skip-ink".equalsIgnoreCase(element)) + { + elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("none", "auto", "all"), ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("text-decoration-style".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("104"))); @@ -1613,6 +1653,16 @@ else if("text-wrap".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("wrap","nowrap","balance"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("text-wrap-mode".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("wrap","nowrap"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } + else if("text-wrap-style".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("auto","balance","stable","pretty","avoid-orphans"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("top".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); @@ -1639,6 +1689,11 @@ else if("unicode-bidi".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal", "embed", "bidi-override", "isolate", "isolate-override", "plaintext"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("translate".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,null,Arrays.asList("40", "40 40", "40 40 72"))); + allelementVerifiers.remove(element); + } else if("vertical-align".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("baseline","sub","super","top","text-top","middle","bottom","text-bottom"),ElementInfo.VISUALMEDIA,Arrays.asList("pe","le"))); @@ -1779,6 +1834,7 @@ private boolean verifyToken(String[] media,String[] elements,CSSPropertyVerifier } + // FIXME: CSS minimizer often remove space between token and !important private int checkImportant(ParsedWord[] words) { if(words.length == 0) return 0; if(words.length >= 1 && words[words.length-1] instanceof SimpleParsedWord) { @@ -3755,7 +3811,7 @@ static class CSSPropertyVerifier public final boolean allowCommaDelimiters; public final Set allowedValues; //immutable HashSet for all String constants that this CSS property can assume like "auto" - // Defaulting Keywords ("initial", "inherit" and "unset") are always accepted + // Defaulting Keywords ("initial", "inherit", "unset", "revert", "revert-layer") are always accepted public final Set allowedMedia; // immutable HashSet for all valid Media for this CSS property. /* @@ -4649,21 +4705,7 @@ public boolean checkValidity(String[] media,String[] elements,ParsedWord[] value ParsedCounter counter = (ParsedCounter)value[0]; if(counter.listType != null) { HashSet listStyleType=new HashSet(); - listStyleType.add("disc"); - listStyleType.add("circle"); - listStyleType.add("square"); - listStyleType.add("decimal"); - listStyleType.add("decimal-leading-zero"); - listStyleType.add("lower-roman"); - listStyleType.add("upper-roman"); - listStyleType.add("lower-greek"); - listStyleType.add("lower-latin"); - listStyleType.add("upper-latin"); - listStyleType.add("armenian"); - listStyleType.add("georgian"); - listStyleType.add("lower-alpha"); - listStyleType.add("upper-alpha"); - listStyleType.add("none"); + listStyleType.addAll(Arrays.asList("disc","circle","square","decimal","decimal-leading-zero","lower-roman","upper-roman","lower-greek","lower-latin","upper-latin","armenian","georgian","lower-alpha","upper-alpha","none","arabic-indic","bengali","cambodian","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","devanagari","disclosure-closed","disclosure-open","ethiopic-numeric","gujarati","gurmukhi","hebrew","hiragana","hiragana-iroha","japanese-formal","japanese-informal","kannada","katakana","katakana-iroha","khmer","korean-hangul-formal","korean-hanja-formal","lao","lower-armenian","malayalam","mongolian","myanmar","oriya","persian","simp-chinese-formal","simp-chinese-informal","tamil","telugu","thai","tibetan","trad-chinese-formal","trad-chinese-informal","upper-armenian")); if(!listStyleType.contains(counter.listType.getDecoded())) return false; } if(counter.separatorString != null && !(ElementInfo.ALLOW_ALL_VALID_STRINGS || ElementInfo.isValidStringDecoded(counter.separatorString.getDecoded()))) diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index 822c08d5392..9fcd5454bd9 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -998,7 +998,13 @@ public class CSSParserTest { propertyTests.put("#x { text-orientation: upright; }", "#x { text-orientation: upright; }"); propertyTests.put("#x { scroll-snap-align: start end; }", "#x { scroll-snap-align: start end; }"); propertyTests.put("#x { scroll-snap-stop: always; }", "#x { scroll-snap-stop: always; }"); - propertyTests.put("#x { scroll-snap-type: both proximity; }", "#x { scroll-snap-type: both proximity; }"); + propertyTests.put("#x { scroll-snap-type: both proximity; } #y { scroll-snap-type: x; }", "#x { scroll-snap-type: both proximity; } #y { scroll-snap-type: x; }"); + propertyTests.put("#box1:hover {rotate: 90deg;} #box2:hover {rotate: y 180deg;} #box3:hover {rotate: 1 2 1 360deg;} #box4:hover {rotate: none;}", "#box1:hover {rotate: 90deg;} #box2:hover {rotate: y 180deg;} #box3:hover {rotate: 1 2 1 360deg;} #box4:hover {rotate: none;}"); + propertyTests.put("#a {rotate: 1;} #b {rotate: 0.1;} #c {rotate: 1 2 3 4;} #d {rotate: x;}", "#a {} #b {} #c {} #d {}"); + propertyTests.put("#a {object-position: top;} #b {object-position: 25% 75%;} #c {object-position: 1cm top;} #d {object-position: bottom 10px right 20px;}", "#a {object-position: top;} #b {object-position: 25% 75%;} #c {object-position: 1cm top;} #d {object-position: bottom 10px right 20px;}"); + propertyTests.put("#a {background-position: top;} #b {background-position: 25% 75%;} #c {background-position: 1cm top;} #d {background-position: bottom 10px right 20px;}", "#a {background-position: top;} #b {background-position: 25% 75%;} #c {background-position: 1cm top;} #d {background-position: bottom 10px right 20px;}"); + propertyTests.put("#a {object-position: top top;} #b {object-position: 25% top 75% left;} #c {object-position: left 10px right 20px;} #d {object-position: top right bottom left;}", "#a {} #b {} #c {} #d {}"); + propertyTests.put("td { border-inline-end: 2px dotted; border-block-start: medium dashed blue }", "td { border-inline-end: 2px dotted; border-block-start: medium dashed blue }"); // text-emphasis propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException