From 8584c214b270483e296a4213e8efd418baac8d90 Mon Sep 17 00:00:00 2001 From: Torusrxxx Date: Thu, 17 Oct 2024 12:53:51 +0000 Subject: [PATCH 01/20] Allow bdi and main elements and auto value of dir attribute --- src/freenet/client/filter/HTMLFilter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/freenet/client/filter/HTMLFilter.java b/src/freenet/client/filter/HTMLFilter.java index 0a7d55afe0e..d0f0e6136cf 100644 --- a/src/freenet/client/filter/HTMLFilter.java +++ b/src/freenet/client/filter/HTMLFilter.java @@ -985,6 +985,7 @@ private static Map getAllowedTagVerifiers() "listing", "plaintext", "center", + "bdi", "bdo", "aside", "header", @@ -995,7 +996,8 @@ private static Map getAllowedTagVerifiers() "hgroup", "wbr", "summary", - "details"}; + "details", + "main"}; for (String x: group2) allowedTagsVerifiers.put( x, @@ -2291,7 +2293,7 @@ Map sanitizeHash(Map h, // lang, xml:lang and dir can go on anything // lang or xml:lang = language [ "-" country [ "-" variant ] ] // The variant can be just about anything; no way to test (avian) - if (x.equals("xml:lang") ||x.equals("lang") || (x.equals("dir") && (o instanceof String) && (((String)o).equalsIgnoreCase("ltr") || ((String)o).equalsIgnoreCase("rtl")))) { + if (x.equals("xml:lang") ||x.equals("lang") || (x.equals("dir") && (o instanceof String) && (((String)o).equalsIgnoreCase("ltr") || ((String)o).equalsIgnoreCase("rtl") || ((String)o).equalsIgnoreCase("auto")))) { if(logDEBUG) Logger.debug(this, "HTML Filter is putting attribute: "+x+" = "+o); hn.put(x, o); } From 7cf31335c4580a0dde2527bb2c3ff3389683b6d3 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sat, 19 Oct 2024 11:50:55 +0800 Subject: [PATCH 02/20] Add elements figcaption,figure,mark,rp,rt,ruby --- src/freenet/client/filter/HTMLFilter.java | 78 ++++++++++--------- .../client/filter/ContentFilterTest.java | 34 ++++---- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/src/freenet/client/filter/HTMLFilter.java b/src/freenet/client/filter/HTMLFilter.java index d0f0e6136cf..f9157a36d60 100644 --- a/src/freenet/client/filter/HTMLFilter.java +++ b/src/freenet/client/filter/HTMLFilter.java @@ -953,51 +953,57 @@ private static Map getAllowedTagVerifiers() emptyStringArray)); String[] group2 = { - "span", - "address", - "em", - "strong", - "dfn", - "code", - "samp", - "kbd", - "var", - "cite", "abbr", "acronym", - "sub", - "sup", - "dt", - "dd", - "tt", - "i", + "address", + "article", + "aside", "b", - "big", - "small", - "strike", - "s", - "u", - "noframes", - "fieldset", -// Delete . So we can at least see the non-scripting code. -// "noscript", - "xmp", - "listing", - "plaintext", - "center", "bdi", "bdo", - "aside", + "big", + "center", + "cite", + "code", + "dd", + "details", + "dfn", + "dt", + "em", + "fieldset", + "figcaption", + "figure", + "footer", "header", + "hgroup", + "i", + "kbd", + "listing", + "main", + "mark", "nav", - "footer", - "article", + "noframes", + // Delete . So we can at least see the non-scripting code. + //"noscript", + "plaintext", + "rp", + "rt", + "ruby", + "s", + "samp", "section", - "hgroup", - "wbr", + "small", + "span", + "strike", + "strong", + "sub", "summary", - "details", - "main"}; + "sup", + "tt", + "u", + "var", + "wbr", + "xmp"}; for (String x: group2) allowedTagsVerifiers.put( x, diff --git a/test/freenet/client/filter/ContentFilterTest.java b/test/freenet/client/filter/ContentFilterTest.java index a2a861920d2..e4bb89ce73a 100644 --- a/test/freenet/client/filter/ContentFilterTest.java +++ b/test/freenet/client/filter/ContentFilterTest.java @@ -115,7 +115,8 @@ public class ContentFilterTest { private static final String SPAN_WITH_STYLE = ""; - private static final String HTML5_TAGS = "
TLDR
Too Long Didn’t Read
"; + private static final String HTML5_TAGS = "
TLDR
Too Long Didn’t Read
Fig.1
"; + private static final String HTML5_BDI_RUBY = "ایران, NorthKoreaNorth Korea"; private static final String BASE_HREF = ""; private static final String BAD_BASE_HREF = ""; @@ -138,6 +139,10 @@ public class ContentFilterTest { HTML_VIDEO_TAG + HTML_AUDIO_TAG, HTML_AUDIO_TAG + HTML_AUDIO_TAG); + private static void testOneHTMLFilter(String html) throws Exception { + assertEquals(html, htmlFilter(html)); + } + @Test public void testHTMLFilter() throws Exception { if (TestProperty.VERBOSE) { @@ -146,7 +151,7 @@ public void testHTMLFilter() throws Exception { // General sanity checks // is "relativization" working? - assertEquals(INTERNAL_RELATIVE_LINK, htmlFilter(INTERNAL_RELATIVE_LINK)); + testOneHTMLFilter(INTERNAL_RELATIVE_LINK); assertEquals(INTERNAL_RELATIVE_LINK, htmlFilter(INTERNAL_RELATIVE_LINK, true)); assertEquals(INTERNAL_RELATIVE_LINK1, htmlFilter(INTERNAL_RELATIVE_LINK1, true)); assertEquals(INTERNAL_RELATIVE_LINK, htmlFilter(INTERNAL_ABSOLUTE_LINK)); @@ -157,15 +162,15 @@ public void testHTMLFilter() throws Exception { // regression testing // bug #710 - assertEquals(ANCHOR_TEST, htmlFilter(ANCHOR_TEST)); - assertEquals(ANCHOR_TEST_EMPTY, htmlFilter(ANCHOR_TEST_EMPTY)); - assertEquals(ANCHOR_TEST_SPECIAL, htmlFilter(ANCHOR_TEST_SPECIAL)); + testOneHTMLFilter(ANCHOR_TEST); + testOneHTMLFilter(ANCHOR_TEST_EMPTY); + testOneHTMLFilter(ANCHOR_TEST_SPECIAL); assertEquals(ANCHOR_TEST_SPECIAL2_RESULT, htmlFilter(ANCHOR_TEST_SPECIAL2)); // bug #2496 - assertEquals(ANCHOR_RELATIVE1, htmlFilter(ANCHOR_RELATIVE1)); - assertEquals(ANCHOR_RELATIVE2, htmlFilter(ANCHOR_RELATIVE2)); - assertEquals(ANCHOR_FALSE_POS1, htmlFilter(ANCHOR_FALSE_POS1)); - assertEquals(ANCHOR_FALSE_POS2, htmlFilter(ANCHOR_FALSE_POS2)); + testOneHTMLFilter(ANCHOR_RELATIVE1); + testOneHTMLFilter(ANCHOR_RELATIVE2); + testOneHTMLFilter(ANCHOR_FALSE_POS1); + testOneHTMLFilter(ANCHOR_FALSE_POS2); // EVIL HACK TEST for #2496 + #2451 assertEquals(ANCHOR_MIXED_RESULT, htmlFilter(ANCHOR_MIXED)); // bug #2451 @@ -176,7 +181,7 @@ public void testHTMLFilter() throws Exception { assertTrue(htmlFilter(PREVENT_EXTERNAL_ACCESS_CSS_SIMPLE).contains("div { }")); assertTrue(htmlFilter(PREVENT_EXTERNAL_ACCESS_CSS_ESCAPE).contains("div { }")); assertTrue(htmlFilter(PREVENT_EXTERNAL_ACCESS_CSS_CASE).contains("div { }")); - assertEquals(WHITELIST_STATIC_CONTENT, htmlFilter(WHITELIST_STATIC_CONTENT)); + testOneHTMLFilter(WHITELIST_STATIC_CONTENT); assertEquals(XHTML_VOIDELEMENTC, htmlFilter(XHTML_VOIDELEMENT)); assertEquals(XHTML_INCOMPLETEDOCUMENTC, htmlFilter(XHTML_INCOMPLETEDOCUMENT)); assertEquals(XHTML_IMPROPERNESTINGC, htmlFilter(XHTML_IMPROPERNESTING)); @@ -193,12 +198,13 @@ public void testHTMLFilter() throws Exception { assertEquals(FRAME_SRC_CHARSET_BADC, htmlFilter(FRAME_SRC_CHARSET_BAD, true)); assertEquals(FRAME_SRC_CHARSET_BAD1C, htmlFilter(FRAME_SRC_CHARSET_BAD1, true)); - assertEquals(CSS_SPEC_EXAMPLE1, htmlFilter(CSS_SPEC_EXAMPLE1)); + testOneHTMLFilter(CSS_SPEC_EXAMPLE1); - assertEquals(SPAN_WITH_STYLE, htmlFilter(SPAN_WITH_STYLE)); - assertEquals(HTML5_TAGS, htmlFilter(HTML5_TAGS)); + testOneHTMLFilter(SPAN_WITH_STYLE); + testOneHTMLFilter(HTML5_TAGS); + testOneHTMLFilter(HTML5_BDI_RUBY); - assertEquals(BASE_HREF, htmlFilter(BASE_HREF)); + testOneHTMLFilter(BASE_HREF); assertEquals(DELETED_BASE_HREF, htmlFilter(BAD_BASE_HREF)); assertEquals(DELETED_BASE_HREF, htmlFilter(BAD_BASE_HREF2)); assertEquals(DELETED_BASE_HREF, htmlFilter(BAD_BASE_HREF3)); From 35a00bfb79eabbc068c1db8c948b416aeb817d7f Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sat, 19 Oct 2024 12:37:33 +0800 Subject: [PATCH 03/20] Add CSS global values revert,revert-layer, attribute writing-mode, update attributes unicode-bidi,caret-color --- src/freenet/client/filter/CSSTokenizerFilter.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 92cdf1591e9..cf1c5010b19 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -322,6 +322,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("word-break"); allelementVerifiers.add("word-spacing"); allelementVerifiers.add("word-wrap"); + allelementVerifiers.add("writing-mode"); allelementVerifiers.add("z-index"); @@ -775,7 +776,7 @@ else if("caption-side".equalsIgnoreCase(element)) allelementVerifiers.remove(element); } else if ("caret-color".equalsIgnoreCase(element)) { - elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto", "transparent"), ElementInfo.VISUALMEDIA, Arrays.asList("co"))); + elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto", "transparent", "currentcolor"), ElementInfo.VISUALMEDIA, Arrays.asList("co"))); allelementVerifiers.remove(element); } else if("clear".equalsIgnoreCase(element)) @@ -1559,7 +1560,7 @@ else if("transform-origin".equalsIgnoreCase(element)) } else if("unicode-bidi".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("normal", "embed", "bidi-override"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal", "embed", "bidi-override", "isolate", "isolate-override", "plaintext"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("vertical-align".equalsIgnoreCase(element)) @@ -1620,6 +1621,11 @@ else if("word-wrap".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal", "break-word", "anywhere"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("writing-mode".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("horizontal-tb", "vertical-rl", "vertical-lr", "lr", "lr-tb", "rl", "tb", "tb-lr", "tb-rl"),ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); + } else if("z-index".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("in"))); @@ -3914,7 +3920,7 @@ public boolean checkValidity(String[] media,String[] elements,ParsedWord[] words // CSS Property has one of the explicitly defined values return true; } - if (lowerCaseWord.equals("initial") || lowerCaseWord.equals("inherit") || lowerCaseWord.equals("unset")) { + if (lowerCaseWord.equals("initial") || lowerCaseWord.equals("inherit") || lowerCaseWord.equals("unset") || lowerCaseWord.equals("revert") || lowerCaseWord.equals("revert-layer")) { // CSS Property is one of the Defaulting Keywords (http://www.w3.org/TR/css3-cascade/#defaulting-keywords) return true; } From 453a446216fd71be6ec464e60ae7192e4be81c94 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sat, 19 Oct 2024 18:07:54 +0800 Subject: [PATCH 04/20] Add CSS colors rebeccapurple and rgba modern format --- src/freenet/client/filter/FilterUtils.java | 139 ++++++++---------- .../client/filter/FilterUtilsTest.java | 22 +++ 2 files changed, 86 insertions(+), 75 deletions(-) diff --git a/src/freenet/client/filter/FilterUtils.java b/src/freenet/client/filter/FilterUtils.java index a8ed13cfd16..9140bce988c 100644 --- a/src/freenet/client/filter/FilterUtils.java +++ b/src/freenet/client/filter/FilterUtils.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.regex.Pattern; public class FilterUtils { private static volatile boolean logDEBUG; @@ -289,6 +290,7 @@ else if(value.contains("rad")) SVGcolorKeywords.add("whitesmoke"); SVGcolorKeywords.add("yellow"); SVGcolorKeywords.add("yellowgreen"); + SVGcolorKeywords.add("rebeccapurple"); // CSS Colors Level 4: #663399 } private final static HashSet CSScolorKeywords=new HashSet(); static @@ -315,34 +317,34 @@ else if(value.contains("rad")) } private final static HashSet CSSsystemColorKeywords=new HashSet(); static { - CSScolorKeywords.add("ActiveBorder"); - CSScolorKeywords.add("ActiveCaption"); - CSScolorKeywords.add("AppWorkspace"); - CSScolorKeywords.add("Background"); - CSScolorKeywords.add("ButtonFace"); - CSScolorKeywords.add("ButtonHighlight"); - CSScolorKeywords.add("ButtonShadow"); - CSScolorKeywords.add("ButtonText"); - CSScolorKeywords.add("CaptionText"); - CSScolorKeywords.add("GrayText"); - CSScolorKeywords.add("Highlight"); - CSScolorKeywords.add("HighlightText"); - CSScolorKeywords.add("InactiveBorder"); - CSScolorKeywords.add("InactiveCaption"); - CSScolorKeywords.add("InactiveCaptionText"); - CSScolorKeywords.add("InfoBackground"); - CSScolorKeywords.add("InfoText"); - CSScolorKeywords.add("Menu"); - CSScolorKeywords.add("MenuText"); - CSScolorKeywords.add("Scrollbar"); - CSScolorKeywords.add("ThreeDDarkShadow"); - CSScolorKeywords.add("ThreeDFace"); - CSScolorKeywords.add("ThreeDHighlight"); - CSScolorKeywords.add("ThreeDLightShadow"); - CSScolorKeywords.add("ThreeDShadow"); - CSScolorKeywords.add("Window"); - CSScolorKeywords.add("WindowFrame"); - CSScolorKeywords.add("WindowText"); + CSScolorKeywords.add("activeborder"); + CSScolorKeywords.add("activecaption"); + CSScolorKeywords.add("appworkspace"); + CSScolorKeywords.add("background"); + CSScolorKeywords.add("buttonface"); + CSScolorKeywords.add("buttonhighlight"); + CSScolorKeywords.add("buttonshadow"); + CSScolorKeywords.add("buttontext"); + CSScolorKeywords.add("captiontext"); + CSScolorKeywords.add("graytext"); + CSScolorKeywords.add("highlight"); + CSScolorKeywords.add("highlighttext"); + CSScolorKeywords.add("inactiveborder"); + CSScolorKeywords.add("inactivecaption"); + CSScolorKeywords.add("inactivecaptiontext"); + CSScolorKeywords.add("infobackground"); + CSScolorKeywords.add("infotext"); + CSScolorKeywords.add("menu"); + CSScolorKeywords.add("menutext"); + CSScolorKeywords.add("scrollbar"); + CSScolorKeywords.add("threeddarkshadow"); + CSScolorKeywords.add("threedface"); + CSScolorKeywords.add("threedhighlight"); + CSScolorKeywords.add("threedlightshadow"); + CSScolorKeywords.add("threedshadow"); + CSScolorKeywords.add("window"); + CSScolorKeywords.add("windowframe"); + CSScolorKeywords.add("windowtext"); } public static boolean isValidCSSShape(String value) { @@ -370,70 +372,57 @@ public static boolean isValidCSSShape(String value) public static boolean isMedia(String media) { return cssMedia.contains(media); } + + public static final Pattern hexColorPattern = Pattern.compile("#(?>[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})", Pattern.CASE_INSENSITIVE); + public static boolean isColor(String value) { - value=value.trim(); + value=value.trim().toLowerCase(); if(CSScolorKeywords.contains(value) || CSSsystemColorKeywords.contains(value) || SVGcolorKeywords.contains(value)) return true; if(value.indexOf('#')==0) { - - if(value.length()==4) + return hexColorPattern.matcher(value).matches(); + } + if((value.startsWith("rgb(") || value.startsWith("rgba(")) && value.indexOf(')')==value.length()-1) + { + // rgba is an alias to rgb + if(value.contains(",")) { - try{ - Integer.valueOf(value.substring(1,2),16).intValue(); - Integer.valueOf(value.substring(2,3),16).intValue(); - Integer.valueOf(value.substring(3,4),16).intValue(); - return true; - } - catch(Exception e) + // Legacy format rgba(r,g,b,a) + String[] colorParts=value.substring(value.indexOf("(")+1,value.length()-1).split(","); + if(colorParts.length!=3&&colorParts.length!=4) + return false; + for(int i=0; i<3; i++) { + if(!(isPercentage(colorParts[i].trim()) || isInteger(colorParts[i].trim()))) + return false; } - - } - else if(value.length()==7) - { - - try{ - Integer.valueOf(value.substring(1,3),16).intValue(); - Integer.valueOf(value.substring(3,5),16).intValue(); - Integer.valueOf(value.substring(5,7),16).intValue(); + if(colorParts.length<=3 || isNumber(colorParts[3])) return true; + }else{ + if(value.contains("/")){ + // Modern format rgba(r g b / a) + String alphaPart=value.substring(value.indexOf("/")+1,value.length()-1).trim(); + if(!alphaPart.isEmpty() && !isPercentage(alphaPart) && !isNumber(alphaPart) && !alphaPart.equalsIgnoreCase("none")) + return false; + value=value.substring(0,value.indexOf("/"))+")"; // Strip alpha value, proceed to the following tests + } + // Modern format rgba(r g b) + String[] colorParts=value.substring(value.indexOf("(")+1,value.length()-1).split(" "); + if(colorParts.length!=3) { + return false; } - catch(Exception e) + for(int i=0; i<3; i++) { + String trimmed = colorParts[i].trim(); + if(!(trimmed.equalsIgnoreCase("none") || isPercentage(trimmed) || (isInteger(trimmed) && isIntegerInRange(trimmed, 0, 255)))) + return false; } - } - } - if(value.indexOf("rgb(")==0 && value.indexOf(')')==value.length()-1) - { - String[] colorParts=value.substring(4,value.length()-1).split(","); - if(colorParts.length!=3) - return false; - boolean isValidColorParts=true; - for(int i=0; i Date: Sat, 19 Oct 2024 18:39:46 +0800 Subject: [PATCH 05/20] Add fonts system-ui,ui-serif,ui-sans-serif,ui-monospace,ui-rounded,emoji,math,fangsong --- src/freenet/client/filter/ElementInfo.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/freenet/client/filter/ElementInfo.java b/src/freenet/client/filter/ElementInfo.java index 91c40b34867..2ee48004675 100644 --- a/src/freenet/client/filter/ElementInfo.java +++ b/src/freenet/client/filter/ElementInfo.java @@ -182,13 +182,22 @@ public class ElementInfo { "new york6" ))); + // https://developer.mozilla.org/en-US/docs/Web/CSS/font-family public static final Set GENERIC_FONT_KEYWORDS = Collections.unmodifiableSet(new HashSet(Arrays.asList( "serif", "sans-serif", "cursive", "fantasy", - "monospace" + "monospace", + "system-ui", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded", + "emoji", + "math", + "fangsong" ))); public static final Set GENERIC_VOICE_KEYWORDS = From 8617f30b0f65ffb2043b50ae9b36c3af8987cafe Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sat, 19 Oct 2024 18:53:23 +0800 Subject: [PATCH 06/20] Add more pseudo classes --- src/freenet/client/filter/ElementInfo.java | 26 +++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/freenet/client/filter/ElementInfo.java b/src/freenet/client/filter/ElementInfo.java index 2ee48004675..128ccf01b6e 100644 --- a/src/freenet/client/filter/ElementInfo.java +++ b/src/freenet/client/filter/ElementInfo.java @@ -215,8 +215,8 @@ public class ElementInfo { "nth-last-child", "nth-of-type", "nth-last-of-type", - "link", - "visited", + "link", // inverse of visited, probably should be blocked? + "visited", // privacy risk (see BANNED_PSEUDOCLASS below) "hover", "active", "checked", @@ -227,7 +227,27 @@ public class ElementInfo { "first-letter", "before", "after", - "target" + "target", + "any-link", + "default", + "defined", + "disabled", + "empty", + "enabled", + "focus-visible", + "dir", + "indeterminate", + "in-range", + "invalid", + "only-child", + "only-of-type", + "optional", + "out-of-range", + "placeholder-shown", + "read-only", + "read-write", + "required", + "root" ))); public static final Set BANNED_PSEUDOCLASS = From ba0d9117245eaede001c92821481615807005719 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sat, 19 Oct 2024 18:54:40 +0800 Subject: [PATCH 07/20] Add inline-start,inline-end values of float CSS attribute --- src/freenet/client/filter/CSSTokenizerFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index cf1c5010b19..6273441fc0a 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -989,7 +989,7 @@ else if("empty-cells".equalsIgnoreCase(element)) } else if("float".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("left","right","none"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("left","right","none","inline-start","inline-end"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if ("flex".equalsIgnoreCase(element)) { // flex: none | ? || From 278a10f10b12bc15409b9908671439d6e0a493cf Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Sun, 20 Oct 2024 00:33:39 +0800 Subject: [PATCH 08/20] Update CSS attributes clear,overflow,text-underline-position --- src/freenet/client/filter/CSSTokenizerFilter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 6273441fc0a..d38e77b3136 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -781,7 +781,7 @@ else if("caption-side".equalsIgnoreCase(element)) } else if("clear".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none","left","right","both"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none","left","right","both","inline-start","inline-end"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("clip".equalsIgnoreCase(element)) @@ -1254,7 +1254,7 @@ else if("outline".equalsIgnoreCase(element)) } else if("overflow".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("visible","hidden","scroll","auto"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("visible","hidden","scroll","auto","clip"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("padding-top".equalsIgnoreCase(element)) @@ -1529,7 +1529,7 @@ else if("text-transform".equalsIgnoreCase(element)) } else if("text-underline-position".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("auto","under","alphabetic","over"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("auto","under","left","right"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("text-wrap".equalsIgnoreCase(element)) From 86f216dd022a8f9e6fa61e5175ec2ed12a74da06 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Tue, 22 Oct 2024 18:18:24 +0800 Subject: [PATCH 09/20] Fix StringIndexOutOfBoundsException when filtering "tr:tenth-child {}" --- src/freenet/client/filter/ElementInfo.java | 79 ++++++------------- test/freenet/client/filter/CSSParserTest.java | 42 ++++++++-- .../client/filter/FilterUtilsTest.java | 14 +++- 3 files changed, 72 insertions(+), 63 deletions(-) diff --git a/src/freenet/client/filter/ElementInfo.java b/src/freenet/client/filter/ElementInfo.java index 128ccf01b6e..29051f825dc 100644 --- a/src/freenet/client/filter/ElementInfo.java +++ b/src/freenet/client/filter/ElementInfo.java @@ -4,6 +4,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; public class ElementInfo { @@ -53,42 +54,6 @@ public class ElementInfo { "button" ))); - // FIXME add some more languages. - public static final Set LANGUAGES = - Collections.unmodifiableSet(new HashSet(Arrays.asList( - "az", - "be", - "bg", - "cs", - "de", - "el", - "en", - "es", - "fi", - "fr", - "id", - "it", - "ja", - "ka", - "kk", - "ky", - "lv", - "mo", - "nl", - "no", - "pl", - "pt", - "ro", - "ru", - "sv", - "tl", - "tr", - "tt", - "uk", - "zh-hans", - "zh-hant" - ))); - public static final Set MEDIA = Collections.unmodifiableSet(new HashSet(Arrays.asList( "all", @@ -215,14 +180,13 @@ public class ElementInfo { "nth-last-child", "nth-of-type", "nth-last-of-type", - "link", // inverse of visited, probably should be blocked? + "link", // inverse of visited (see BANNED_PSEUDOCLASS below) "visited", // privacy risk (see BANNED_PSEUDOCLASS below) "hover", "active", "checked", "focus", "focus-within", - "lang", "first-line", "first-letter", "before", @@ -230,13 +194,12 @@ public class ElementInfo { "target", "any-link", "default", - "defined", - "disabled", + "defined", // Javascript only (BANNED_PSEUDOCLASS) + "disabled", // forms "empty", - "enabled", + "enabled", // forms "focus-visible", - "dir", - "indeterminate", + "indeterminate", // forms "in-range", "invalid", "only-child", @@ -278,7 +241,10 @@ public class ElementInfo { // is considered too much of a danger, so we scrub that pseudoclass. // // [1] http://lcamtuf.coredump.cx/css_calc/ - "visited" + "link", + "visited", + // Javascript only + "defined" ))); public static boolean isSpecificFontFamily(String font) { @@ -467,7 +433,7 @@ public static boolean isBannedPseudoClass(String cname) cname=cname.toLowerCase(); return BANNED_PSEUDOCLASS.contains(cname); } - + public static boolean isValidPseudoClass(String cname) { if(cname.indexOf(':') != -1) { @@ -482,28 +448,35 @@ public static boolean isValidPseudoClass(String cname) return true; - else if(cname.contains("lang") && LANGUAGES.contains(getPseudoClassArg(cname, "lang"))) + else if(cname.startsWith("lang") && Pattern.matches("[\\w\\-*]{1,30}", getPseudoClassArg(cname, "lang"))) { - // FIXME accept unknown languages as long as they are [a-z-] + // More than 8000 valid BCP-47 language codes. Just let through all of them. return true; } - else if(cname.contains("nth-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-child"))) + else if(cname.startsWith("nth-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-child"))) return true; - else if(cname.contains("nth-last-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-child"))) + else if(cname.startsWith("nth-last-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-child"))) return true; - else if(cname.contains("nth-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-of-type"))) + else if(cname.startsWith("nth-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-of-type"))) return true; - else if(cname.contains("nth-last-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-of-type"))) + else if(cname.startsWith("nth-last-of-type") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-of-type"))) return true; - + else if(cname.startsWith("dir")) { + String arg = getPseudoClassArg(cname, "dir"); + return arg.equalsIgnoreCase("ltr") || arg.equalsIgnoreCase("rtl"); + } + return false; - } + } + public static String getPseudoClassArg(String cname, String cname_sans_arg) { String arg=""; int cnameIndex=cname.indexOf(cname_sans_arg); int firstIndex=cname.indexOf('('); int secondIndex=cname.lastIndexOf(')'); + if(cnameIndex == -1 || firstIndex == -1 || secondIndex == -1) + return ""; if(cname.substring(cnameIndex + cname_sans_arg.length(), firstIndex).trim().isEmpty() && cname.substring(0, cnameIndex).trim().isEmpty() && cname.substring(secondIndex + 1, cname.length()).trim().isEmpty()) { arg=CSSTokenizerFilter.removeOuterQuotes(cname.substring(firstIndex+1,secondIndex).trim()); diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index ff9ea15df7e..ac1816d1e36 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -39,7 +39,7 @@ public class CSSParserTest { private final static HashMap CSS1_SELECTOR= new HashMap<>(); static { CSS1_SELECTOR.put("h1 {}","h1"); - CSS1_SELECTOR.put("h1:link {}","h1:link"); + CSS1_SELECTOR.put("h1:link {color:transparent}",""); CSS1_SELECTOR.put("h1:visited {}",""); CSS1_SELECTOR.put("h1.warning {}","h1.warning"); CSS1_SELECTOR.put("h1#myid {}","h1#myid"); @@ -90,9 +90,9 @@ public class CSSParserTest { CSS2_SELECTOR.put("div > p:FIRST-CHILD { text-indent: 0 }", "div>p:FIRST-CHILD { text-indent: 0 }"); CSS2_SELECTOR.put("p:first-child em { font-weight : bold }", "p:first-child em { font-weight: bold }"); CSS2_SELECTOR.put("* > a:first-child {}", "*>a:first-child {}"); - CSS2_SELECTOR.put(":link { color: red }", ":link { color: red }"); // REDFLAG: link vs visited is safe for Freenet as there is no scripting. // If there was scripting it would not be safe, although datastore probing is probably the greater threat. + CSS2_SELECTOR.put(":link { color: red }", ""); CSS2_SELECTOR.put("a.external:visited { color: blue }", ""); CSS2_SELECTOR.put("a:focus:hover { background: white }", "a:focus:hover { background: white }"); CSS2_SELECTOR.put("p:first-line { text-transform: uppercase;}", "p:first-line { text-transform: uppercase;}"); @@ -137,6 +137,10 @@ public class CSSParserTest { CSS2_BAD_SELECTOR.add("h1[foo,=bar] {}"); CSS2_BAD_SELECTOR.add("h1:langblahblah(fr) {}"); + // java.lang.StringIndexOutOfBoundsException + CSS2_BAD_SELECTOR.add("h1:golang {}"); + // missing argument + CSS2_BAD_SELECTOR.add("h1:lang {}"); // THE FOLLOWING ARE VALID BUT DISALLOWED // ] inside string inside attribute selector: way too confusing for parsers. @@ -222,8 +226,23 @@ public class CSSParserTest { // Whitespace not supported at all. CSS3_BAD_SELECTOR.add("tr:nth-child( n+2) {}"); CSS3_BAD_SELECTOR.add("tr:nth-child(n + 2) {}"); + // java.lang.StringIndexOutOfBoundsException + CSS3_BAD_SELECTOR.add("tr:tenth-child {}"); + CSS3_BAD_SELECTOR.add("tr:tenth-child(2n+1) {}"); } + private final static HashMap CSS_SELECTOR_LEVEL4= new HashMap<>(); + static { + CSS_SELECTOR_LEVEL4.put("div:dir(ltr) {}", "div:dir(ltr)"); + CSS_SELECTOR_LEVEL4.put("div:dir(rtl) {}", "div:dir(rtl)"); + } + + private final static HashSet CSS_BAD_SELECTOR_LEVEL4= new HashSet<>(); + static { + CSS_BAD_SELECTOR_LEVEL4.add("div:bidir(ltr) {}"); + CSS_BAD_SELECTOR_LEVEL4.add("div:dir {}"); // missing ltr or rtl + } + private static final String CSS_STRING_NEWLINES = "* { content: \"this string does not terminate\n}\nbody {\nbackground: url(http://www.google.co.uk/intl/en_uk/images/logo.gif); }\n\" }"; private static final String CSS_STRING_NEWLINESC = "* {}\nbody { }\n"; @@ -751,7 +770,8 @@ public class CSSParserTest { propertyTests.put("p { text-indent: 3em }", "p { text-indent: 3em }"); propertyTests.put("p { text-indent: 33% }", "p { text-indent: 33% }"); propertyTests.put("div.important { text-align: center }", "div.important { text-align: center }"); - propertyTests.put("a:visited,a:link { text-decoration: underline }", "a:link { text-decoration: underline }"); + propertyTests.put("a:visited,a:link { text-decoration: underline }", ""); + propertyTests.put("a:any-link { text-decoration: underline }", "a:any-link { text-decoration: underline }"); propertyTests.put("blockquote { text-decoration: underline overline line-through blink } h1 { text-decoration: none } h2 { text-decoration: inherit }","blockquote { text-decoration: underline overline line-through blink } h1 { text-decoration: none } h2 { text-decoration: inherit }"); propertyTests.put("blockquote { letter-spacing: 0.1em }", "blockquote { letter-spacing: 0.1em }"); propertyTests.put("blockquote { letter-spacing: normal }", "blockquote { letter-spacing: normal }"); @@ -776,10 +796,8 @@ public class CSSParserTest { propertyTests.put("table { empty-cells: show }", "table { empty-cells: show }"); // User interface - propertyTests.put(":link,:visited { cursor: url(example.svg#linkcursor) url(hyper.cur) pointer }", ":link { cursor: url(\"example.svg#linkcursor\") url(\"hyper.cur\") pointer }"); - propertyTests.put(":link,:visited { cursor: url(example.svg#linkcursor), url(hyper.cur), pointer }", ":link { cursor: url(\"example.svg#linkcursor\"), url(\"hyper.cur\"), pointer }"); - propertyTests.put(":link,:visited { cursor: url(example.svg#linkcursor) 2 5, url(hyper.cur), pointer }", ":link { cursor: url(\"example.svg#linkcursor\") 2 5, url(\"hyper.cur\"), pointer }"); - propertyTests.put(":link,:visited { cursor: url(example.svg#linkcursor) 2, url(hyper.cur), pointer }", ":link { }"); + propertyTests.put(":link,:visited { cursor: url(example.svg#linkcursor) url(hyper.cur) pointer }", ""); + propertyTests.put(":any-link { cursor: url(example.svg#linkcursor) 2, url(hyper.cur), pointer }", ":any-link { }"); // UI colors propertyTests.put("p { color: WindowText; background-color: Window }", "p { color: WindowText; background-color: Window }"); @@ -960,7 +978,17 @@ public void testCSS3Selector() throws IOException, URISyntaxException { testCssSelectorFiltering(CSS3_SELECTOR); testBadSelectorFiltering(CSS3_BAD_SELECTOR); } + + @Test + public void testCSS4Selector() throws IOException, URISyntaxException { + testCssSelectorFiltering(CSS_SELECTOR_LEVEL4); + } + @Test + public void testCSS4SelectorBad() throws IOException, URISyntaxException { + testBadSelectorFiltering(CSS_BAD_SELECTOR_LEVEL4); + } + @Test public void testNewlines() throws IOException, URISyntaxException { assertEquals( diff --git a/test/freenet/client/filter/FilterUtilsTest.java b/test/freenet/client/filter/FilterUtilsTest.java index b13ac40907c..5367d0e63d9 100755 --- a/test/freenet/client/filter/FilterUtilsTest.java +++ b/test/freenet/client/filter/FilterUtilsTest.java @@ -54,6 +54,7 @@ public void testValidColors() { assertTrue(FilterUtils.isColor("#123")); assertTrue(FilterUtils.isColor("#123F")); assertTrue(FilterUtils.isColor("#123456ff")); + assertTrue(FilterUtils.isColor("rgb(0,10,255)")); assertTrue(FilterUtils.isColor("rgb(0 10 255)")); assertTrue(FilterUtils.isColor("rgba(100 200 255 / 0.25)")); assertTrue(FilterUtils.isColor("rgba(010 00200 255 / 25%)")); @@ -62,8 +63,15 @@ public void testValidColors() { @Test public void testInvalidColors() { - assertFalse(FilterUtils.isColor("rgb(0.1 0.2 0.3)")); - assertFalse(FilterUtils.isColor("rgb(/)")); - assertFalse(FilterUtils.isColor("#ABCDEFGH")); + assertFalse(FilterUtils.isColor("rgb(0.1 0.2 0.3)")); // should be between 0 and 255, not 0.1 + assertFalse(FilterUtils.isColor("rgb(")); // completely empty; don't trigger out of range access here! + assertFalse(FilterUtils.isColor("rgb()")); // completely empty; don't trigger out of range access here! + assertFalse(FilterUtils.isColor("rgb(/)")); // completely empty; don't trigger out of range access here! + assertFalse(FilterUtils.isColor("#ABCDEFGH")); // #ABCDEF followed by G and H + assertFalse(FilterUtils.isColor("112233")); // missing # + assertFalse(FilterUtils.isColor("#12")); // not #RGB + assertFalse(FilterUtils.isColor("#12345")); // not #RGBA neither #RRGGBB + assertFalse(FilterUtils.isColor("#1234567")); // not #RRGGBB neither #RRGGBBAA + assertFalse(FilterUtils.isColor("url(/KSK@foo)")); // url not color! } } From 46f36f7c9d2473dc510e85ea498107832be881a9 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Wed, 23 Oct 2024 00:19:46 +0800 Subject: [PATCH 10/20] Add font size xxx-large and selector tests --- .../client/filter/CSSTokenizerFilter.java | 14 ++---- src/freenet/client/filter/ElementInfo.java | 43 ++++++++++------- test/freenet/client/filter/CSSParserTest.java | 48 ++++++++++++++++++- 3 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index d38e77b3136..877c1a97305 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -25,7 +25,6 @@ import freenet.support.Fields; import freenet.support.Logger; import freenet.support.api.Bucket; -import freenet.support.io.Closer; import freenet.support.io.FileBucket; /** Comprehensive CSS2.1 filter. The old jflex-based filter was very far @@ -1030,7 +1029,7 @@ else if("font-family".equalsIgnoreCase(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","larger","smaller"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); + 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"))); allelementVerifiers.remove(element); } else if("font-style".equalsIgnoreCase(element)) @@ -4871,18 +4870,15 @@ public static void main(String arg[]) throws Throwable { fout.delete(); final Bucket inputBucket = new FileBucket(fin, true, false, false, false); final Bucket outputBucket = new FileBucket(fout, false, true, false, false); - InputStream inputStream = null; - OutputStream outputStream = null; - try { - inputStream = inputBucket.getInputStream(); - outputStream = outputBucket.getOutputStream(); + try ( + InputStream inputStream = inputBucket.getInputStream(); + OutputStream outputStream = outputBucket.getOutputStream() + ) { Logger.setupStdoutLogging(Logger.LogLevel.DEBUG, ""); ContentFilter.filter(inputStream, outputStream, "text/css", new URI("http://127.0.0.1:8888/freenet:USK@ZupQjDFZSc3I4orBpl1iTEAPZKo2733RxCUbZ2Q7iH0,EO8Tuf8SP3lnDjQdAPdCM2ve2RaUEN8m-hod3tQ5oQE,AQACAAE/jFreesite/19/Style/"), null, null, null, null); } finally { - Closer.close(inputStream); - Closer.close(outputStream); inputBucket.free(); outputBucket.free(); } diff --git a/src/freenet/client/filter/ElementInfo.java b/src/freenet/client/filter/ElementInfo.java index 29051f825dc..e31cf259e7f 100644 --- a/src/freenet/client/filter/ElementInfo.java +++ b/src/freenet/client/filter/ElementInfo.java @@ -184,7 +184,7 @@ public class ElementInfo { "visited", // privacy risk (see BANNED_PSEUDOCLASS below) "hover", "active", - "checked", + "checked", // forms "focus", "focus-within", "first-line", @@ -193,23 +193,23 @@ public class ElementInfo { "after", "target", "any-link", - "default", + "default", // forms "defined", // Javascript only (BANNED_PSEUDOCLASS) "disabled", // forms "empty", "enabled", // forms "focus-visible", "indeterminate", // forms - "in-range", - "invalid", + "in-range", // forms + "invalid", // forms "only-child", "only-of-type", - "optional", - "out-of-range", - "placeholder-shown", - "read-only", - "read-write", - "required", + "optional", // forms + "out-of-range", // forms + "placeholder-shown", // forms + "read-only", // forms + "read-write", // forms + "required", // forms "root" ))); @@ -427,11 +427,16 @@ public static boolean isBannedPseudoClass(String cname) // Pseudo-classes can be chained, at least dynamic ones can, see CSS2.1 section 5.11.3 String[] split = cname.split(":"); for(String s : split) - if(isBannedPseudoClass(s)) return true; + if(isBannedPseudoClass2(s)) return true; return false; + } else { + return isBannedPseudoClass2(cname); } - cname=cname.toLowerCase(); - return BANNED_PSEUDOCLASS.contains(cname); + } + + private static boolean isBannedPseudoClass2(String cname) + { + return BANNED_PSEUDOCLASS.contains(cname.toLowerCase()); } public static boolean isValidPseudoClass(String cname) @@ -440,20 +445,23 @@ public static boolean isValidPseudoClass(String cname) // Pseudo-classes can be chained, at least dynamic ones can, see CSS2.1 section 5.11.3 String[] split = cname.split(":"); for(String s : split) - if(!isValidPseudoClass(s)) return false; + if(!isValidPseudoClass2(s)) return false; return true; + } else { + return isValidPseudoClass2(cname); } + } + + private static boolean isValidPseudoClass2(String cname) + { cname=cname.toLowerCase(); if(PSEUDOCLASS.contains(cname)) return true; - - else if(cname.startsWith("lang") && Pattern.matches("[\\w\\-*]{1,30}", getPseudoClassArg(cname, "lang"))) { // More than 8000 valid BCP-47 language codes. Just let through all of them. return true; } - else if(cname.startsWith("nth-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-child"))) return true; else if(cname.startsWith("nth-last-child") && FilterUtils.isNth(getPseudoClassArg(cname, "nth-last-child"))) @@ -466,7 +474,6 @@ else if(cname.startsWith("dir")) { String arg = getPseudoClassArg(cname, "dir"); return arg.equalsIgnoreCase("ltr") || arg.equalsIgnoreCase("rtl"); } - return false; } diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index ac1816d1e36..63e659c9a35 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -235,12 +235,58 @@ public class CSSParserTest { static { CSS_SELECTOR_LEVEL4.put("div:dir(ltr) {}", "div:dir(ltr)"); CSS_SELECTOR_LEVEL4.put("div:dir(rtl) {}", "div:dir(rtl)"); + CSS_SELECTOR_LEVEL4.put(":target {}", ":target"); + CSS_SELECTOR_LEVEL4.put(":any-link {}", ":any-link"); + CSS_SELECTOR_LEVEL4.put(":empty {}", ":empty"); + CSS_SELECTOR_LEVEL4.put(":focus-visible {}", ":focus-visible"); + CSS_SELECTOR_LEVEL4.put(":only-child {}", ":only-child"); + CSS_SELECTOR_LEVEL4.put(":only-of-type {}", ":only-of-type"); + CSS_SELECTOR_LEVEL4.put(":root {font-size: xxx-large;}", ":root {font-size: xxx-large;}"); + // forms + CSS_SELECTOR_LEVEL4.put("input:default {}", "input:default"); + CSS_SELECTOR_LEVEL4.put("input:disabled {}", "input:disabled"); + CSS_SELECTOR_LEVEL4.put("input:enabled {}", "input:enabled"); + CSS_SELECTOR_LEVEL4.put("input:indeterminate {}", "input:indeterminate"); + CSS_SELECTOR_LEVEL4.put("input:in-range {}", "input:in-range"); + CSS_SELECTOR_LEVEL4.put("input:invalid {}", "input:invalid"); + CSS_SELECTOR_LEVEL4.put("input:optional {}", "input:optional"); + CSS_SELECTOR_LEVEL4.put("input:out-of-range {}", "input:out-of-range"); + CSS_SELECTOR_LEVEL4.put("input:placeholder-shown {}", "input:placeholder-shown"); + CSS_SELECTOR_LEVEL4.put("input:read-only {}", "input:read-only"); + CSS_SELECTOR_LEVEL4.put("input:read-write {}", "input:read-write"); + CSS_SELECTOR_LEVEL4.put("input:required {}", "input:required"); } private final static HashSet CSS_BAD_SELECTOR_LEVEL4= new HashSet<>(); static { + // not dir CSS_BAD_SELECTOR_LEVEL4.add("div:bidir(ltr) {}"); - CSS_BAD_SELECTOR_LEVEL4.add("div:dir {}"); // missing ltr or rtl + // missing ltr or rtl + CSS_BAD_SELECTOR_LEVEL4.add("div:dir {}"); + // these selectors don't have arguments + CSS_BAD_SELECTOR_LEVEL4.add(":target() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":any-link() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":empty() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":focus-visible() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":only-child() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":only-of-type() {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":root() {}"); + // these forms selectors don't have arguments + CSS_BAD_SELECTOR_LEVEL4.add("input:default() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:disabled() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:enabled() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:indeterminate() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:in-range() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:invalid() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:optional() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:out-of-range() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:placeholder-shown() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:read-only() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:read-write() {}"); + CSS_BAD_SELECTOR_LEVEL4.add("input:required() {}"); + // banned + CSS_BAD_SELECTOR_LEVEL4.add(":defined {}"); + CSS_BAD_SELECTOR_LEVEL4.add(":defined() {}"); } private static final String CSS_STRING_NEWLINES = "* { content: \"this string does not terminate\n}\nbody {\nbackground: url(http://www.google.co.uk/intl/en_uk/images/logo.gif); }\n\" }"; From 5e0d013b6891d24d81375ab683f7fa8bea0169b7 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Thu, 24 Oct 2024 16:01:43 +0800 Subject: [PATCH 11/20] Add background-blend-mode and mix-blend-mode and CSS tests --- .../client/filter/CSSTokenizerFilter.java | 17 +++++++++++++++-- test/freenet/client/filter/CSSParserTest.java | 12 ++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 877c1a97305..f3a99e70789 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -118,6 +118,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("align-self"); allelementVerifiers.add("azimuth"); allelementVerifiers.add("background-attachment"); + allelementVerifiers.add("background-blend-mode"); allelementVerifiers.add("background-clip"); allelementVerifiers.add("background-color"); allelementVerifiers.add("background-image"); @@ -242,6 +243,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("max-width"); allelementVerifiers.add("min-height"); allelementVerifiers.add("min-width"); + allelementVerifiers.add("mix-blend-mode"); allelementVerifiers.add("nav-down"); allelementVerifiers.add("nav-left"); allelementVerifiers.add("nav-right"); @@ -331,7 +333,7 @@ public static T[] concat(T[] a, T[] b) { * Array for storing additional Verifier objects for validating Regular expressions in CSS Property value * e.g. [ | transparent]{1,4}. It is explained in detail in CSSPropertyVerifier class */ - private final static CSSPropertyVerifier[] auxilaryVerifiers=new CSSPropertyVerifier[148]; + private final static CSSPropertyVerifier[] auxilaryVerifiers=new CSSPropertyVerifier[149]; static { /*CSSPropertyVerifier(String[] allowedValues,String[] possibleValues,String expression,boolean onlyValueVerifier)*/ @@ -454,6 +456,11 @@ else if("background-attachment".equalsIgnoreCase(element)){ elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("60<1,65535>"), true,true)); allelementVerifiers.remove(element); } + else if("background-blend-mode".equalsIgnoreCase(element)){ + auxilaryVerifiers[148] = new CSSPropertyVerifier(Arrays.asList("normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"), null, null, null, true); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("148<1,2>"), true,true)); + allelementVerifiers.remove(element); + } else if("background-clip".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("61<1,65535>"), true,true)); @@ -1196,7 +1203,13 @@ else if("min-width".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); allelementVerifiers.remove(element); - } else if ("nav-down".equalsIgnoreCase(element)) { + } + else if("mix-blend-mode".equalsIgnoreCase(element)){ + auxilaryVerifiers[148] = new CSSPropertyVerifier(Arrays.asList("normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"), null, null, null, true); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("148<1,2>"), true,true)); + allelementVerifiers.remove(element); + } + else if ("nav-down".equalsIgnoreCase(element)) { elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto"), ElementInfo.VISUALINTERACTIVEMEDIA, null, Arrays.asList("143 144?"))); allelementVerifiers.remove(element); } else if ("nav-left".equalsIgnoreCase(element)) { diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index 63e659c9a35..7b216cbded7 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -966,6 +966,18 @@ public class CSSParserTest { propertyTests.put("div { transition-delay: \"test\"; }", "div { }"); propertyTests.put("div { transition-property: \"test\"; }", "div { }"); propertyTests.put("div { transition-timing-function: \"test\"; }", "div { }"); + + // writing mode + propertyTests.put("#a { writing-mode: vertical-rl; text-underline-position: left; } #b { writing-mode: horizontal-tb; text-underline-position: auto; }", "#a { writing-mode: vertical-rl; text-underline-position: left; } #b { writing-mode: horizontal-tb; text-underline-position: auto; }"); + + // Compositing and Blending + propertyTests.put("#foo { background: url(\"1.png\"); background-blend-mode: darken; }", "#foo { background: url(\"1.png\"); background-blend-mode: darken; }"); + propertyTests.put("#foo {mix-blend-mode: luminosity; }", "#foo {mix-blend-mode: luminosity; }"); + + // new property values + propertyTests.put("div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}", "div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}"); + propertyTests.put("#a {unicode-bidi: isolate;}", "#a {unicode-bidi: isolate;}"); + propertyTests.put("textarea#x {caret-color: currentcolor;}", "textarea#x {caret-color: currentcolor;}"); } FilterMIMEType cssMIMEType; From e33b53439f930c30ce55c59104854063c9ea1c3c Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Fri, 25 Oct 2024 18:19:36 +0800 Subject: [PATCH 12/20] Fix text-shadow parsing --- src/freenet/client/filter/CSSTokenizerFilter.java | 4 ++-- test/freenet/client/filter/CSSParserTest.java | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index f3a99e70789..a03b2b1c853 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -375,7 +375,7 @@ public static T[] concat(T[] a, T[] b) { auxilaryVerifiers[78]=new CSSPropertyVerifier(null,null,Arrays.asList("70<1,2>"),null,true); // - auxilaryVerifiers[79]=new CSSPropertyVerifier(null, null, Arrays.asList("74a73"), null, true); + auxilaryVerifiers[79]=new CSSPropertyVerifier(null, null, Arrays.asList("73 72 72 72","73 72 72","72 72 72 73","72 72 73","72 72"), null, true); // auxilaryVerifiers[85]=new CSSPropertyVerifier(Arrays.asList("normal"), Arrays.asList("le","pe"), null, null, true); @@ -1531,7 +1531,7 @@ else if("text-overflow".equalsIgnoreCase(element)) } else if("text-shadow".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,null,Arrays.asList("79<0,65535>"),true,true)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,null,Arrays.asList("79"),true,true)); allelementVerifiers.remove(element); } else if("text-transform".equalsIgnoreCase(element)) diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index 7b216cbded7..89c42da5c88 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -978,6 +978,13 @@ public class CSSParserTest { propertyTests.put("div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}", "div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}"); propertyTests.put("#a {unicode-bidi: isolate;}", "#a {unicode-bidi: isolate;}"); propertyTests.put("textarea#x {caret-color: currentcolor;}", "textarea#x {caret-color: currentcolor;}"); + // text-shadow + propertyTests.put("#x { text-shadow: 1px 1px 2px black; }", "#x { text-shadow: 1px 1px 2px black; }"); + propertyTests.put("#x { text-shadow: #fc0 1px 0 10px; }", "#x { text-shadow: #fc0 1px 0 10px; }"); + propertyTests.put("#x { text-shadow: 5px 5px #558abb; }", "#x { text-shadow: 5px 5px #558abb; }"); + propertyTests.put("#x { text-shadow: white 2px 5px; }", "#x { text-shadow: white 2px 5px; }"); + propertyTests.put("#x { text-shadow: 5px 10px; }", "#x { text-shadow: 5px 10px; }"); + propertyTests.put("#x { text-shadow: 1px 1px 2px 1px black; }", "#x { }"); } FilterMIMEType cssMIMEType; From c814ae14fcb753209c1df32e95606ddcaf4717b1 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Fri, 25 Oct 2024 20:55:14 +0800 Subject: [PATCH 13/20] Add attributes block-size,inline-size, length values min-content,max-content,fit-content --- .../client/filter/CSSTokenizerFilter.java | 23 ++++++++++++++++--- test/freenet/client/filter/CSSParserTest.java | 7 ++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index a03b2b1c853..fa04addebc4 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -127,6 +127,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("background-repeat"); allelementVerifiers.add("background-size"); allelementVerifiers.add("background"); + allelementVerifiers.add("block-size"); allelementVerifiers.add("border-collapse"); allelementVerifiers.add("border-color"); allelementVerifiers.add("border-top-color"); @@ -223,6 +224,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("font"); allelementVerifiers.add("hanging-punctuation"); allelementVerifiers.add("height"); + allelementVerifiers.add("inline-size"); allelementVerifiers.add("justify-content"); allelementVerifiers.add("justify-items"); allelementVerifiers.add("justify-self"); @@ -519,6 +521,11 @@ else if("background".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("6a7a8a9a10"))); allelementVerifiers.remove(element); } + else if("block-size".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); + allelementVerifiers.remove(element); + } else if("border-collapse".equalsIgnoreCase(element)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("collapse","separate"),ElementInfo.VISUALMEDIA)); @@ -1097,6 +1104,11 @@ else if("hanging-punctuation".equalsIgnoreCase(element)) allelementVerifiers.remove(element); } else if("height".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); + 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); @@ -4003,10 +4015,15 @@ public boolean checkValidity(String[] media,String[] elements,ParsedWord[] words } } - if(words[0] instanceof ParsedIdentifier && isColor) { - if(FilterUtils.isColor(((ParsedIdentifier)words[0]).original)) + if(words[0] instanceof ParsedIdentifier) { + String value = ((ParsedIdentifier)words[0]).original; + if(isColor && FilterUtils.isColor(value)) { return true; - + } + if(isLength && (value.equalsIgnoreCase("min-content") || value.equalsIgnoreCase("max-content") || value.equalsIgnoreCase("fit-content"))) { + //TODO: support fit-content(20em) + return true; + } } if(isURI && words[0] instanceof ParsedURL) diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index 89c42da5c88..ecd483b6b1a 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -968,8 +968,9 @@ public class CSSParserTest { propertyTests.put("div { transition-timing-function: \"test\"; }", "div { }"); // writing mode - propertyTests.put("#a { writing-mode: vertical-rl; text-underline-position: left; } #b { writing-mode: horizontal-tb; text-underline-position: auto; }", "#a { writing-mode: vertical-rl; text-underline-position: left; } #b { writing-mode: horizontal-tb; text-underline-position: auto; }"); - + propertyTests.put("#a { writing-mode: vertical-rl; text-underline-position: left; }", "#a { writing-mode: vertical-rl; text-underline-position: left; }"); + propertyTests.put("#b { writing-mode: horizontal-tb; text-underline-position: auto; inline-size: max-content; block-size: 200px; }", "#b { writing-mode: horizontal-tb; text-underline-position: auto; inline-size: max-content; block-size: 200px; }"); + // Compositing and Blending propertyTests.put("#foo { background: url(\"1.png\"); background-blend-mode: darken; }", "#foo { background: url(\"1.png\"); background-blend-mode: darken; }"); propertyTests.put("#foo {mix-blend-mode: luminosity; }", "#foo {mix-blend-mode: luminosity; }"); @@ -985,6 +986,8 @@ public class CSSParserTest { propertyTests.put("#x { text-shadow: white 2px 5px; }", "#x { text-shadow: white 2px 5px; }"); propertyTests.put("#x { text-shadow: 5px 10px; }", "#x { text-shadow: 5px 10px; }"); propertyTests.put("#x { text-shadow: 1px 1px 2px 1px black; }", "#x { }"); + // not possible to parse a comma separated list? + //propertyTests.put("#x { text-shadow: 1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue; }", "#x { text-shadow: 1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue; }"); } FilterMIMEType cssMIMEType; From cc74d9874b997eb27b916693994fd001c198584c Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Mon, 28 Oct 2024 18:12:32 +0800 Subject: [PATCH 14/20] fix NullPointerException with text-emphasis, fix deprecated attributes word-wrap,white-space-collapsing --- .../client/filter/CSSTokenizerFilter.java | 31 +++++++++---------- test/freenet/client/filter/CSSParserTest.java | 7 +++++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index fa04addebc4..66210a8579c 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -259,6 +259,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("outline-width"); allelementVerifiers.add("outline"); allelementVerifiers.add("overflow"); + allelementVerifiers.add("overflow-wrap"); allelementVerifiers.add("padding-top"); allelementVerifiers.add("padding-right"); allelementVerifiers.add("padding-bottom"); @@ -319,7 +320,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("voice-family"); allelementVerifiers.add("volume"); allelementVerifiers.add("white-space"); - allelementVerifiers.add("white-space-collapsing"); + allelementVerifiers.add("white-space-collapse"); allelementVerifiers.add("widows"); allelementVerifiers.add("width"); allelementVerifiers.add("word-break"); @@ -358,10 +359,12 @@ public static T[] concat(T[] a, T[] b) { // auxilaryVerifiers[64]=new CSSPropertyVerifier(null,Arrays.asList("le", "pe"),null,null,true); + // + auxilaryVerifiers[73]=new CSSPropertyVerifier(null, Arrays.asList("co"), null, null, true); + // auxilaryVerifiers[71]=new CSSPropertyVerifier(Arrays.asList("inset"), null, null, null, true); auxilaryVerifiers[72]=new CSSPropertyVerifier(null, Arrays.asList("le"), null, null, true); - auxilaryVerifiers[73]=new CSSPropertyVerifier(null, Arrays.asList("co"), null, null, true); auxilaryVerifiers[74]=new CSSPropertyVerifier(null, null, Arrays.asList("72<1,4>"), null, true); auxilaryVerifiers[75]=new CSSPropertyVerifier(null, null, Arrays.asList("71a74a73"), null, true); @@ -388,15 +391,13 @@ public static T[] concat(T[] a, T[] b) { auxilaryVerifiers[102] = new CSSPropertyVerifier(Arrays.asList("line-through"), null, null, null, true); auxilaryVerifiers[115] = new CSSPropertyVerifier(Arrays.asList("none"),null,null,Arrays.asList("100a101a102")); auxilaryVerifiers[116] = new CSSPropertyVerifier(Arrays.asList("blink"), null, null, null, true); - // - auxilaryVerifiers[103] = new CSSPropertyVerifier(null, Arrays.asList("co"), null, null, true); // auxilaryVerifiers[104] = new CSSPropertyVerifier(Arrays.asList("solid", "double", "dotted", "dashed", "wave"), null, null, null, true); // auxilaryVerifiers[105]=new CSSPropertyVerifier(Arrays.asList("filled","open"),null,null,null,true); auxilaryVerifiers[106]=new CSSPropertyVerifier(Arrays.asList("dot","circle","double-circle","triangle","sesame"),null,null,null,true); - auxilaryVerifiers[107]=new CSSPropertyVerifier(Arrays.asList("none"),ElementInfo.VISUALMEDIA,Arrays.asList("st"),Arrays.asList("105a106")); + auxilaryVerifiers[107]=new CSSPropertyVerifier(Arrays.asList("none"),null,Arrays.asList("st"),Arrays.asList("105a106")); // and // auto | | || [ ? && ] @@ -1458,13 +1459,13 @@ else if("text-autospace".equalsIgnoreCase(element)) } else if("text-decoration".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("115a103a104a116"))); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("115a73a104a116"))); allelementVerifiers.remove(element); } else if("text-decoration-color".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("103"))); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("73"))); allelementVerifiers.remove(element); } @@ -1492,13 +1493,13 @@ else if("text-decoration-style".equalsIgnoreCase(element)) } else if("text-emphasis".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("103a107"))); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("73a107"))); allelementVerifiers.remove(element); } else if("text-emphasis-color".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("103"))); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("73"))); allelementVerifiers.remove(element); } @@ -1548,7 +1549,7 @@ else if("text-shadow".equalsIgnoreCase(element)) } else if("text-transform".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("capitalize","uppercase","lowercase","none","fullwidth","large-kana"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("capitalize","uppercase","lowercase","none","fullwidth","full-size-kana","math-auto"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("text-underline-position".equalsIgnoreCase(element)) @@ -1612,12 +1613,9 @@ else if("white-space".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal","pre","nowrap","pre-wrap","pre-line"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } - else if("white-space-collapsing".equalsIgnoreCase(element)) + else if("white-space-collapse".equalsIgnoreCase(element)) { - auxilaryVerifiers[80]=new CSSPropertyVerifier(Arrays.asList("preserve","preserve-break"),null,null,null,true); - auxilaryVerifiers[81]=new CSSPropertyVerifier(Arrays.asList("trim-inner"),null,null,null,true); - auxilaryVerifiers[82]=new CSSPropertyVerifier(null,null,Arrays.asList("80a81"),null,true); - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("collapse" ,"discard"),null,ElementInfo.VISUALMEDIA,Arrays.asList("82"))); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("preserve","preserve-break","collapse","discard","break-spaces"),null,ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("widows".equalsIgnoreCase(element)) @@ -1640,8 +1638,9 @@ else if("word-spacing".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(null,null,ElementInfo.VISUALMEDIA,Arrays.asList("85<1,3>"))); allelementVerifiers.remove(element); } - else if("word-wrap".equalsIgnoreCase(element)) + else if("word-wrap".equalsIgnoreCase(element) || "overflow-wrap".equalsIgnoreCase(element)) { + // word-wrap was a Microsoft extension that got renamed to overflow-wrap in CSS Text Module Level 3. elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal", "break-word", "anywhere"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index ecd483b6b1a..a49f2d59ad2 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -818,6 +818,7 @@ public class CSSParserTest { propertyTests.put("div.important { text-align: center }", "div.important { text-align: center }"); propertyTests.put("a:visited,a:link { text-decoration: underline }", ""); propertyTests.put("a:any-link { text-decoration: underline }", "a:any-link { text-decoration: underline }"); + propertyTests.put("a:any-link { text-decoration: underline red }", "a:any-link { text-decoration: underline red }"); propertyTests.put("blockquote { text-decoration: underline overline line-through blink } h1 { text-decoration: none } h2 { text-decoration: inherit }","blockquote { text-decoration: underline overline line-through blink } h1 { text-decoration: none } h2 { text-decoration: inherit }"); propertyTests.put("blockquote { letter-spacing: 0.1em }", "blockquote { letter-spacing: 0.1em }"); propertyTests.put("blockquote { letter-spacing: normal }", "blockquote { letter-spacing: normal }"); @@ -979,6 +980,12 @@ public class CSSParserTest { propertyTests.put("div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}", "div { overflow: clip; clear: inline-end; text-decoration: revert; float: inline-end;}"); propertyTests.put("#a {unicode-bidi: isolate;}", "#a {unicode-bidi: isolate;}"); propertyTests.put("textarea#x {caret-color: currentcolor;}", "textarea#x {caret-color: currentcolor;}"); + propertyTests.put("div { word-wrap: anywhere; overflow-wrap: anywhere; }", "div { word-wrap: anywhere; overflow-wrap: anywhere; }"); + propertyTests.put("div { white-space-collapse: collapse; }", "div { white-space-collapse: collapse; }"); + // text-emphasis + propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException + propertyTests.put("#x { text-emphasis: filled triangle blue; }", "#x { text-emphasis: filled triangle blue; }"); + propertyTests.put("#x { text-emphasis-style: triangle; text-emphasis-color: blue; }", "#x { text-emphasis-style: triangle; text-emphasis-color: blue; }"); // text-shadow propertyTests.put("#x { text-shadow: 1px 1px 2px black; }", "#x { text-shadow: 1px 1px 2px black; }"); propertyTests.put("#x { text-shadow: #fc0 1px 0 10px; }", "#x { text-shadow: #fc0 1px 0 10px; }"); From 9591dba276ce65715294fcd95727e58f0089f311 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Mon, 28 Oct 2024 23:46:07 +0800 Subject: [PATCH 15/20] Add tab-size,font-kerning, update word-break --- src/freenet/client/filter/CSSTokenizerFilter.java | 14 +++++++++++++- test/freenet/client/filter/CSSParserTest.java | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 66210a8579c..d8d8f9afd7b 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -217,6 +217,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("flex-wrap"); allelementVerifiers.add("float"); allelementVerifiers.add("font-family"); + allelementVerifiers.add("font-kerning"); allelementVerifiers.add("font-size"); allelementVerifiers.add("font-style"); allelementVerifiers.add("font-variant"); @@ -287,6 +288,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("speech-rate"); allelementVerifiers.add("stress"); allelementVerifiers.add("table-layout"); + allelementVerifiers.add("tab-size"); allelementVerifiers.add("text-align"); allelementVerifiers.add("text-align-last"); allelementVerifiers.add("text-autospace"); @@ -1042,6 +1044,11 @@ else if("font-family".equalsIgnoreCase(element)) elementVerifiers.put(element,new FontPropertyVerifier(false)); allelementVerifiers.remove(element); } + else if("font-kerning".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto","none","normal"),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"))); @@ -1437,6 +1444,11 @@ else if("table-layout".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("auto","fixed"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } + else if("tab-size".equalsIgnoreCase(element)) + { + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,Arrays.asList("le","in"))); + allelementVerifiers.remove(element); + } else if("text-align".equalsIgnoreCase(element)) { // FIXME: We don't support "one character" as the spec says http://www.w3.org/TR/css3-text/#text-align0 elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("start","end","left","right","center","justify","match-parent"),ElementInfo.VISUALMEDIA)); @@ -1630,7 +1642,7 @@ else if("width".equalsIgnoreCase(element)) } else if("word-break".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal","break-all","hyphenate"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("normal","break-all","hyphenate","keep-all"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("word-spacing".equalsIgnoreCase(element)) diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index a49f2d59ad2..9acadd68036 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -982,6 +982,9 @@ public class CSSParserTest { propertyTests.put("textarea#x {caret-color: currentcolor;}", "textarea#x {caret-color: currentcolor;}"); propertyTests.put("div { word-wrap: anywhere; overflow-wrap: anywhere; }", "div { word-wrap: anywhere; overflow-wrap: anywhere; }"); propertyTests.put("div { white-space-collapse: collapse; }", "div { white-space-collapse: collapse; }"); + propertyTests.put("#a { word-break: keep-all; font-kerning: none; }", "#a { word-break: keep-all; font-kerning: none; }"); + propertyTests.put("#a { tab-size: 4; }", "#a { tab-size: 4; }"); + propertyTests.put("#a { tab-size: 12pt; }", "#a { tab-size: 12pt; }"); // text-emphasis propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException propertyTests.put("#x { text-emphasis: filled triangle blue; }", "#x { text-emphasis: filled triangle blue; }"); From 47e72cc8fa37ac982748d4b45833a6cdec3569b9 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Tue, 29 Oct 2024 00:10:01 +0800 Subject: [PATCH 16/20] Add object-fit --- src/freenet/client/filter/CSSTokenizerFilter.java | 4 ++++ test/freenet/client/filter/CSSParserTest.java | 1 + 2 files changed, 5 insertions(+) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index d8d8f9afd7b..31d142c39f2 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -251,6 +251,7 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("nav-left"); allelementVerifiers.add("nav-right"); allelementVerifiers.add("nav-up"); + allelementVerifiers.add("object-fit"); allelementVerifiers.add("opacity"); allelementVerifiers.add("order"); allelementVerifiers.add("orphans"); @@ -1241,6 +1242,9 @@ else if ("nav-down".equalsIgnoreCase(element)) { } else if ("nav-up".equalsIgnoreCase(element)) { elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("auto"), ElementInfo.VISUALINTERACTIVEMEDIA, null, Arrays.asList("143 144?"))); allelementVerifiers.remove(element); + } else if ("object-fit".equalsIgnoreCase(element)) { + elementVerifiers.put(element, new CSSPropertyVerifier(Arrays.asList("contain","cover","fill","none","scale-down"), ElementInfo.VISUALMEDIA)); + allelementVerifiers.remove(element); } else if("opacity".equalsIgnoreCase(element)) { diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index 9acadd68036..d02ace6243e 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -985,6 +985,7 @@ public class CSSParserTest { propertyTests.put("#a { word-break: keep-all; font-kerning: none; }", "#a { word-break: keep-all; font-kerning: none; }"); propertyTests.put("#a { tab-size: 4; }", "#a { tab-size: 4; }"); propertyTests.put("#a { tab-size: 12pt; }", "#a { tab-size: 12pt; }"); + propertyTests.put("img#a { object-fit: scale-down; }", "img#a { object-fit: scale-down; }"); // text-emphasis propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException propertyTests.put("#x { text-emphasis: filled triangle blue; }", "#x { text-emphasis: filled triangle blue; }"); From 598c52f1bc8a00d864a0d7ccb509e8aa2b25abeb Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Tue, 29 Oct 2024 20:56:28 +0800 Subject: [PATCH 17/20] Support "reversed" attribute of
    element --- src/freenet/client/filter/HTMLFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/client/filter/HTMLFilter.java b/src/freenet/client/filter/HTMLFilter.java index f9157a36d60..495cc20be19 100644 --- a/src/freenet/client/filter/HTMLFilter.java +++ b/src/freenet/client/filter/HTMLFilter.java @@ -1080,7 +1080,7 @@ private static Map getAllowedTagVerifiers() "ol", new CoreTagVerifier( "ol", - new String[] { "type", "compact", "start" }, + new String[] { "type", "compact", "start", "reversed" }, emptyStringArray, emptyStringArray, emptyStringArray, From 5b9f643515014527c7f08c3b05c6f99fd3647c9a Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Thu, 31 Oct 2024 12:17:49 +0800 Subject: [PATCH 18/20] Add more types to list-style-type and also allow string --- src/freenet/client/filter/CSSTokenizerFilter.java | 7 ++++--- test/freenet/client/filter/CSSParserTest.java | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 31d142c39f2..beadfb22394 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -357,6 +357,9 @@ public static T[] concat(T[] a, T[] b) { // auxilaryVerifiers[15]=new CSSPropertyVerifier(Arrays.asList("transparent"),Arrays.asList("co"),null,null,true); + //list-style-type + auxilaryVerifiers[35]=new CSSPropertyVerifier(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"),Arrays.asList("st"),null,null,true); + // auxilaryVerifiers[61]=new CSSPropertyVerifier(Arrays.asList("border-box", "padding-box", "content-box"),null,null,null,true); // @@ -1163,7 +1166,7 @@ else if("list-style-position".equalsIgnoreCase(element)) } else if("list-style-type".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier(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"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("35"))); allelementVerifiers.remove(element); } else if("list-style".equalsIgnoreCase(element)) @@ -1172,8 +1175,6 @@ else if("list-style".equalsIgnoreCase(element)) auxilaryVerifiers[33]=new CSSPropertyVerifier(Arrays.asList("none"),Arrays.asList("ur"),null,null,true); //list-style-position auxilaryVerifiers[34]=new CSSPropertyVerifier(Arrays.asList("inside","outside"),null,null,null,true); - //list-style-type - auxilaryVerifiers[35]=new CSSPropertyVerifier(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"),null,null,null,true); elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("33a34a35"))); allelementVerifiers.remove(element); } diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index d02ace6243e..acb78192f87 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -986,6 +986,9 @@ public class CSSParserTest { propertyTests.put("#a { tab-size: 4; }", "#a { tab-size: 4; }"); propertyTests.put("#a { tab-size: 12pt; }", "#a { tab-size: 12pt; }"); propertyTests.put("img#a { object-fit: scale-down; }", "img#a { object-fit: scale-down; }"); + propertyTests.put("#x { list-style-type: korean-hanja-formal }", "#x { list-style-type: korean-hanja-formal }"); + propertyTests.put("#x { list-style-type: \"*\" }", "#x { list-style-type: \"*\" }"); + // text-emphasis propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException propertyTests.put("#x { text-emphasis: filled triangle blue; }", "#x { text-emphasis: filled triangle blue; }"); From 2dc724b102ef6eecdacb3bdb70bfce32a6bd81b8 Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Mon, 4 Nov 2024 22:45:09 +0800 Subject: [PATCH 19/20] Add max-block-size,max-inline-size,min-block-size,min-inline-size --- .../client/filter/CSSTokenizerFilter.java | 24 +++++++++++++++++++ test/freenet/client/filter/CSSParserTest.java | 1 + 2 files changed, 25 insertions(+) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index beadfb22394..1453773dfbf 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -242,9 +242,13 @@ public static T[] concat(T[] a, T[] b) { allelementVerifiers.add("margin-top"); allelementVerifiers.add("margin-bottom"); allelementVerifiers.add("margin"); + allelementVerifiers.add("max-block-size"); allelementVerifiers.add("max-height"); + allelementVerifiers.add("max-inline-size"); allelementVerifiers.add("max-width"); + allelementVerifiers.add("min-block-size"); allelementVerifiers.add("min-height"); + allelementVerifiers.add("min-inline-size"); allelementVerifiers.add("min-width"); allelementVerifiers.add("mix-blend-mode"); allelementVerifiers.add("nav-down"); @@ -1206,21 +1210,41 @@ else if("margin".equalsIgnoreCase(element)) elementVerifiers.put(element,new CSSPropertyVerifier(null,ElementInfo.VISUALMEDIA,null,Arrays.asList("36<1,4>"))); 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)) { 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)) { elementVerifiers.put(element,new CSSPropertyVerifier(Arrays.asList("auto"),ElementInfo.VISUALMEDIA,Arrays.asList("le","pe"))); diff --git a/test/freenet/client/filter/CSSParserTest.java b/test/freenet/client/filter/CSSParserTest.java index acb78192f87..a1d5736a844 100644 --- a/test/freenet/client/filter/CSSParserTest.java +++ b/test/freenet/client/filter/CSSParserTest.java @@ -988,6 +988,7 @@ public class CSSParserTest { propertyTests.put("img#a { object-fit: scale-down; }", "img#a { object-fit: scale-down; }"); propertyTests.put("#x { list-style-type: korean-hanja-formal }", "#x { list-style-type: korean-hanja-formal }"); propertyTests.put("#x { list-style-type: \"*\" }", "#x { list-style-type: \"*\" }"); + propertyTests.put("#x { max-inline-size: none; min-block-size: auto; }", "#x { max-inline-size: none; min-block-size: auto; }"); // text-emphasis propertyTests.put("#x { text-emphasis: triangle blue; }", "#x { text-emphasis: triangle blue; }"); // java.lang.NullPointerException From 2bc2fc2cc98ddc5055ef12cfb43219d31fd1e3cb Mon Sep 17 00:00:00 2001 From: torusrxxx Date: Wed, 6 Nov 2024 18:31:13 +0800 Subject: [PATCH 20/20] Fix text-wrap having nonstandard values --- src/freenet/client/filter/CSSTokenizerFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/client/filter/CSSTokenizerFilter.java b/src/freenet/client/filter/CSSTokenizerFilter.java index 1453773dfbf..a697c072c04 100644 --- a/src/freenet/client/filter/CSSTokenizerFilter.java +++ b/src/freenet/client/filter/CSSTokenizerFilter.java @@ -1600,7 +1600,7 @@ else if("text-underline-position".equalsIgnoreCase(element)) } else if("text-wrap".equalsIgnoreCase(element)) { - elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("normal","unrestricted","none","suppress"),ElementInfo.VISUALMEDIA)); + elementVerifiers.put(element,new CSSPropertyVerifier( Arrays.asList("wrap","nowrap","balance"),ElementInfo.VISUALMEDIA)); allelementVerifiers.remove(element); } else if("top".equalsIgnoreCase(element))