Skip to content

Commit cbe2411

Browse files
Change how CSS property caching works (unfinished)
1 parent d81b48e commit cbe2411

File tree

11 files changed

+313
-325
lines changed

11 files changed

+313
-325
lines changed

Thready/src/main/java/com/github/webicitybrowser/thready/gui/directive/basics/pool/NestingDirectivePool.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public class NestingDirectivePool implements ComposedDirectivePool<DirectivePool
1717

1818
private final DirectivePool parent;
1919
private final DirectivePool defaultPool = new BasicDirectivePool();
20-
private final List<DirectivePool> subpools = new ArrayList<>(4);
21-
private final List<DirectivePoolListener> subpoolListeners = new ArrayList<>(4);
20+
private final List<DirectivePool> subpools = new ArrayList<>(1);
21+
private final List<DirectivePoolListener> subpoolListeners = new ArrayList<>(1);
2222
private final Set<DirectivePoolListener> listeners = new HashSet<>(1);
2323

2424
public NestingDirectivePool(DirectivePool parent) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.github.webicitybrowser.webicity.renderer.backend.html.cssom;
2+
3+
import java.util.List;
4+
import java.util.Optional;
5+
6+
import com.github.webicitybrowser.spec.css.parser.TokenLike;
7+
import com.github.webicitybrowser.spec.css.rule.CSSRuleList;
8+
import com.github.webicitybrowser.webicity.renderer.backend.html.cssom.imp.CSSOMMappedRuleListImp;
9+
10+
public interface CSSOMMappedRuleList<T> {
11+
12+
<U extends T> PropertyMeta<U> resolveProperty(Class<U> propertyType, RelativeResolver<T> unknownResolver);
13+
14+
Optional<TokenLike[]> resolveVariable(String variableName, RelativeResolver<T> unknownResolver);
15+
16+
interface RelativeResolver<T> {
17+
18+
<U extends T> PropertyMeta<U> resolveParentProperty(Class<U> propertyType);
19+
20+
Optional<TokenLike[]> resolveVariable(String variableName);
21+
22+
}
23+
24+
record PropertyMeta<T>(T resolvedValue, String name, TokenLike[] tokens, boolean important, boolean cacheable, PropertyMeta<T> fallback) {
25+
26+
public static PropertyMeta<?> EMPTY = new PropertyMeta<>(null, null, null, false, false, null);
27+
28+
public boolean present() {
29+
return resolvedValue != null;
30+
}
31+
32+
}
33+
34+
interface PropertyMapper<T> {
35+
List<Class<? extends T>> getPossibleResultantTypes(String name);
36+
List<T> map(String name, TokenLike[] tokens);
37+
Class<? extends T> keyForValue(T value);
38+
}
39+
40+
public static <T> CSSOMMappedRuleList<T> create(CSSRuleList ruleList, PropertyMapper<T> propertyMapper) {
41+
return new CSSOMMappedRuleListImp<>(ruleList, propertyMapper);
42+
}
43+
44+
}

Webicity/src/main/java/com/github/webicitybrowser/webicity/renderer/backend/html/cssom/CSSOMPropertyResolver.java

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.github.webicitybrowser.webicity.renderer.backend.html.cssom.imp;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Optional;
6+
7+
import com.github.webicitybrowser.spec.css.parser.TokenLike;
8+
import com.github.webicitybrowser.spec.css.parser.tokens.IdentToken;
9+
import com.github.webicitybrowser.spec.css.parser.util.TokenUtils;
10+
import com.github.webicitybrowser.spec.css.rule.CSSRule;
11+
import com.github.webicitybrowser.spec.css.rule.CSSRuleList;
12+
import com.github.webicitybrowser.spec.css.rule.Declaration;
13+
import com.github.webicitybrowser.webicity.renderer.backend.html.cssom.CSSOMMappedRuleList;
14+
15+
public class CSSOMMappedRuleListImp<T> implements CSSOMMappedRuleList<T> {
16+
17+
private final PropertyMapper<T> propertyMapper;
18+
19+
private final HashMap<Object, PropertyMeta<T>> resolvedProperties = new HashMap<>();
20+
21+
public CSSOMMappedRuleListImp(CSSRuleList ruleList, PropertyMapper<T> propertyMapper) {
22+
this.propertyMapper = propertyMapper;
23+
recomputeProperties(ruleList);
24+
}
25+
26+
@Override
27+
@SuppressWarnings({"unchecked", "rawtypes"})
28+
public <U extends T> PropertyMeta<U> resolveProperty(Class<U> propertyType, RelativeResolver<T> relativeResolver) {
29+
PropertyMeta<T> resolvedPropertyMeta = followFallbackChain((PropertyMeta) resolvedProperties.get(propertyType), relativeResolver, propertyType);
30+
if (resolvedPropertyMeta == null) {
31+
return (PropertyMeta) PropertyMeta.EMPTY;
32+
}
33+
34+
return (PropertyMeta<U>) resolvedPropertyMeta;
35+
}
36+
37+
@Override
38+
public Optional<TokenLike[]> resolveVariable(String variableName, RelativeResolver<T> relativeResolver) {
39+
PropertyMeta<T> resolvedPropertyMeta = followFallbackChain(resolvedProperties.get(variableName), relativeResolver, null);
40+
if (resolvedPropertyMeta == null || resolvedPropertyMeta.tokens() == null) {
41+
return Optional.empty();
42+
}
43+
44+
return Optional.of(resolvedPropertyMeta.tokens());
45+
}
46+
47+
private void recomputeProperties(CSSRuleList ruleList) {
48+
resolvedProperties.clear();
49+
for (int i = 0; i < ruleList.getLength(); i++) {
50+
CSSRule rule = ruleList.getItem(i);
51+
// TODO: Handle other rule types
52+
if (!(rule instanceof Declaration declaration)) continue;
53+
// TODO: Support var types
54+
TokenLike[] tokens = TokenUtils.stripWhitespace(declaration.getValue());
55+
if (isNonCachedProperty(declaration, tokens)) {
56+
PropertyMeta<T> propertyMeta = new PropertyMeta<T>(null, declaration.getName(), tokens, declaration.isImportant(), false, null);
57+
maybeAddPropertyValue(declaration, propertyMeta);
58+
continue;
59+
}
60+
List<T> propertyValues = propertyMapper.map(declaration.getName(), tokens);
61+
for (T propertyValue: propertyValues) {
62+
PropertyMeta<T> propertyMeta = new PropertyMeta<T>(propertyValue, declaration.getName(), tokens, declaration.isImportant(), true, null);
63+
maybeAddPropertyValue(declaration, propertyMeta);
64+
}
65+
}
66+
}
67+
68+
private void maybeAddPropertyValue(Declaration declaration, PropertyMeta<T> propertyMeta) {
69+
PropertyMeta<T> oldMeta = resolvedProperties.get(declaration.getName());
70+
if (!declaration.isImportant() && oldMeta != null && oldMeta.important()) {
71+
if (oldMeta.present()) return;
72+
resolvedProperties.put(declaration.getName(), oldMeta.fallback());
73+
maybeAddPropertyValue(declaration, propertyMeta);
74+
PropertyMeta<T> newFallbackMeta = resolvedProperties.get(declaration.getName());
75+
PropertyMeta<T> oldMetaWithNewFallback = new PropertyMeta<>(
76+
oldMeta.resolvedValue(), oldMeta.name(), oldMeta.tokens(), oldMeta.important(), oldMeta.cacheable(), newFallbackMeta);
77+
resolvedProperties.put(declaration.getName(), oldMetaWithNewFallback);
78+
79+
return;
80+
}
81+
82+
PropertyMeta<T> newMeta = new PropertyMeta<>(
83+
propertyMeta.resolvedValue(), propertyMeta.name(), propertyMeta.tokens(), declaration.isImportant(), propertyMeta.cacheable(), oldMeta);
84+
85+
if (propertyMeta.present()) {
86+
resolvedProperties.put(propertyMapper.keyForValue(propertyMeta.resolvedValue()), newMeta);
87+
} else if (declaration.getName().startsWith("--")) {
88+
resolvedProperties.put(declaration.getName(), newMeta);
89+
} else {
90+
for (Class<? extends T> possibleType: propertyMapper.getPossibleResultantTypes(declaration.getName())) {
91+
resolvedProperties.put(possibleType, newMeta);
92+
}
93+
}
94+
}
95+
96+
@SuppressWarnings({ "rawtypes", "unchecked" })
97+
private <U extends T> PropertyMeta<U> followFallbackChain(PropertyMeta<U> propertyMeta, RelativeResolver<T> relativeResolver, Class<U> propertyType) {
98+
if (propertyMeta == null || propertyMeta.present()) return propertyMeta;
99+
PropertyMeta<U> currentMeta = new PropertyMeta<U>(null, null, null, false, false, propertyMeta);
100+
while (true) {
101+
currentMeta = currentMeta.fallback();
102+
if (currentMeta == null) return null;
103+
if (propertyType != null && currentMeta.present()) return (PropertyMeta) currentMeta;
104+
if (currentMeta.tokens() == null) continue;
105+
Optional<TokenLike[]> resolvedTokens = CSSOMVariableResolver.resolveVariables(currentMeta.tokens(), relativeResolver);
106+
if (resolvedTokens.isEmpty()) continue;
107+
if (propertyType == null) return new PropertyMeta(null, null, resolvedTokens.get(), currentMeta.important(), false, currentMeta.fallback());
108+
PropertyMeta<U> resolvedSpecial = resolveSpecialProperty(resolvedTokens.get(), relativeResolver, propertyType);
109+
if (resolvedSpecial.present()) return (PropertyMeta) resolvedSpecial;
110+
List<T> propertyValues = propertyMapper.map(currentMeta.name(), resolvedTokens.get());
111+
if (propertyValues.isEmpty()) continue;
112+
for (T propertyValue: propertyValues) {
113+
if (propertyMapper.keyForValue(propertyValue) == propertyType) {
114+
return new PropertyMeta<>((U) propertyValue, propertyMeta.name(), resolvedTokens.get(), currentMeta.important(), false, currentMeta.fallback());
115+
}
116+
}
117+
}
118+
}
119+
120+
@SuppressWarnings({ "rawtypes", "unchecked" })
121+
private <U extends T> PropertyMeta<U> resolveSpecialProperty(TokenLike[] tokenLikes, RelativeResolver<T> relativeResolver, Class<U> propertyType) {
122+
if (isInherit(tokenLikes)) {
123+
return relativeResolver.resolveParentProperty(propertyType);
124+
}
125+
126+
return (PropertyMeta) PropertyMeta.EMPTY;
127+
}
128+
129+
private boolean isNonCachedProperty(Declaration declaration, TokenLike[] tokens) {
130+
boolean isVariable = declaration.getName().startsWith("--");
131+
return isInherit(tokens) || isVariable || CSSOMVariableResolver.hasVariable(tokens);
132+
}
133+
134+
private boolean isInherit(TokenLike[] tokens) {
135+
return tokens.length == 1
136+
&& tokens[0] instanceof IdentToken identToken
137+
&& identToken.getValue().equals("inherit");
138+
}
139+
140+
}

Webicity/src/main/java/com/github/webicitybrowser/webicity/renderer/backend/html/cssom/imp/CSSOMPropertyResolverImp.java

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)