From fdbf044d778a284738100e71436cf0ef0e323fa0 Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 8 Aug 2026 15:02:25 +0200 Subject: [PATCH] Cache CSS media list results --- .../java/eu/faircode/email/ActivityCode.java | 6 +++- .../eu/faircode/email/FragmentCompose.java | 4 ++- .../java/eu/faircode/email/HtmlHelper.java | 31 +++++++++++++------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/ActivityCode.java b/app/src/main/java/eu/faircode/email/ActivityCode.java index c421a57fdc..228a8931ce 100644 --- a/app/src/main/java/eu/faircode/email/ActivityCode.java +++ b/app/src/main/java/eu/faircode/email/ActivityCode.java @@ -55,6 +55,7 @@ import org.jsoup.parser.ParseError; import org.jsoup.parser.ParseErrorList; import org.jsoup.parser.Parser; import org.w3c.dom.css.CSSStyleSheet; +import org.w3c.dom.stylesheets.MediaList; import java.io.File; import java.io.FileInputStream; @@ -62,7 +63,9 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class ActivityCode extends ActivityBase { private WebView wvCode; @@ -356,12 +359,13 @@ public class ActivityCode extends ActivityBase { if (sanitize) { List sheets = HtmlHelper.parseStyles(d.head().select("style")); + Map cache = new HashMap<>(); for (Element element : d.select("*")) { String computed = HtmlHelper.processStyles(context, element.tagName(), element.className(), element.attr("style"), - sheets); + sheets, cache); if (!TextUtils.isEmpty(computed)) element.attr("x-computed", computed); } diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index d2fc5f8b7e..2675860a37 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -177,6 +177,7 @@ import org.jsoup.select.Elements; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; import org.w3c.dom.css.CSSStyleSheet; +import org.w3c.dom.stylesheets.MediaList; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -6371,10 +6372,11 @@ public class FragmentCompose extends FragmentBase { // Apply styles List sheets = HtmlHelper.parseStyles(d.head().select("style")); + Map cache = new HashMap<>(); for (Element element : e.select("*")) { String tag = element.tagName(); String clazz = element.attr("class"); - String style = HtmlHelper.processStyles(context, tag, clazz, null, sheets); + String style = HtmlHelper.processStyles(context, tag, clazz, null, sheets, cache); style = HtmlHelper.mergeStyles(style, element.attr("style")); if (!TextUtils.isEmpty(style)) element.attr("style", style); diff --git a/app/src/main/java/eu/faircode/email/HtmlHelper.java b/app/src/main/java/eu/faircode/email/HtmlHelper.java index c04314da95..113bc2d32c 100644 --- a/app/src/main/java/eu/faircode/email/HtmlHelper.java +++ b/app/src/main/java/eu/faircode/email/HtmlHelper.java @@ -660,11 +660,12 @@ public class HtmlHelper { } // Sanitize styles + Map cache = new HashMap<>(); for (Element element : document.select("*")) { // Class style String tag = element.tagName(); String clazz = element.className(); - String style = processStyles(context, tag, clazz, null, sheets); + String style = processStyles(context, tag, clazz, null, sheets, cache); // Element style style = mergeStyles(style, element.attr("style")); @@ -1855,17 +1856,17 @@ public class HtmlHelper { return sheets; } - static String processStyles(Context context, String tag, String clazz, String style, List sheets) { + static String processStyles(Context context, String tag, String clazz, String style, List sheets, Map cache) { for (CSSStyleSheet sheet : sheets) - if (isScreenMedia(context, sheet.getMedia())) { - style = processStyles(context, null, clazz, style, sheet.getCssRules(), Selector.SAC_ELEMENT_NODE_SELECTOR); - style = processStyles(context, tag, clazz, style, sheet.getCssRules(), Selector.SAC_ELEMENT_NODE_SELECTOR); - style = processStyles(context, tag, clazz, style, sheet.getCssRules(), Selector.SAC_CONDITIONAL_SELECTOR); + if (isScreenMedia(context, sheet.getMedia(), cache)) { + style = processStyles(context, null, clazz, style, sheet.getCssRules(), Selector.SAC_ELEMENT_NODE_SELECTOR, cache); + style = processStyles(context, tag, clazz, style, sheet.getCssRules(), Selector.SAC_ELEMENT_NODE_SELECTOR, cache); + style = processStyles(context, tag, clazz, style, sheet.getCssRules(), Selector.SAC_CONDITIONAL_SELECTOR, cache); } return style; } - private static String processStyles(Context context, String tag, String clazz, String style, CSSRuleList rules, int stype) { + private static String processStyles(Context context, String tag, String clazz, String style, CSSRuleList rules, int stype, Map cache) { for (int i = 0; rules != null && i < rules.getLength(); i++) { CSSRule rule = rules.item(i); switch (rule.getType()) { @@ -1904,15 +1905,25 @@ public class HtmlHelper { case CSSRule.MEDIA_RULE: CSSMediaRuleImpl mrule = (CSSMediaRuleImpl) rule; - if (isScreenMedia(context, mrule.getMedia())) - style = processStyles(context, tag, clazz, style, mrule.getCssRules(), stype); + if (isScreenMedia(context, mrule.getMedia(), cache)) + style = processStyles(context, tag, clazz, style, mrule.getCssRules(), stype, cache); break; } } return style; } - private static boolean isScreenMedia(Context context, MediaList media) { + private static boolean isScreenMedia(Context context, MediaList media, Map cache) { + if (cache != null && cache.containsKey(media)) + return cache.get(media); + + boolean is = _isScreenMedia(context, media); + if (cache != null) + cache.put(media, is); + return is; + } + + private static boolean _isScreenMedia(Context context, MediaList media) { // https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries // https://developers.google.com/gmail/design/reference/supported_css#supported_types if (media instanceof MediaListImpl) {