On Thu, 10 Sep 2026 00:21:45 GMT, Marius Hanl <[email protected]> wrote:
>> modules/javafx.graphics/src/main/java/javafx/scene/CssStyleHelper.java line
>> 100:
>>
>>> 98: // We first recreate the style helper that are stale.
>>> 99: // This usually only happens when a child changes the scene
>>> tree while its CSS is processed.
>>> 100: for (int index = path.length - 1; index > 0; index--) {
>>
>> so my ai buddy suggests the process remains quadratic:
>> while `getStyleableParent()` is used only once, its overloads are called for
>> each stale ancestor. each scans the path to find a user-set font, following
>> to the root, and a new `CacheContainer` scans again.
>
> I fail to understand that. All methods in the `createStyleHelper` will use
> the parent chain we created once. How could we still end up being quadratic?
> We will go through the aprent chain, yes, but we did that before as well
ok, this might be a contrived scenario, but it does illustrate the problem.
the test passes in master and fails with this PR:
@Test
void testQuadraticPerformace() {
scene.getStylesheets().add(toDataURL(
"""
.old { -fx-padding: 11; }
.new { -fx-padding: 22; }
"""));
int depth = 50;
AtomicInteger count = new AtomicInteger();
class TPane extends Pane {
private final SimpleStyleableObjectProperty<Font> font;
private final List<CssMetaData<? extends Styleable, ?>> metadata;
public TPane(CssMetaData<TPane, Font> fontMetadata) {
font = new SimpleStyleableObjectProperty<>(fontMetadata, this,
"font", Font.getDefault());
// we should have a utility for this
ArrayList<CssMetaData<? extends Styleable, ?>> md = new
ArrayList<>(Pane.getClassCssMetaData());
md.add(fontMetadata);
this.metadata = List.copyOf(md);
getStyleClass().add("old");
}
@Override
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return metadata;
}
}
CssMetaData<TPane, Font> fontMD = new
FontCssMetaData<TPane>("-fx-font", Font.getDefault()) {
@Override
public StyleableProperty<Font> getStyleableProperty(TPane p) {
count.incrementAndGet();
return p.font;
}
@Override
public boolean isSettable(TPane p) {
return !p.font.isBound();
}
};
ArrayList<TPane> panes = new ArrayList<>(depth);
TPane top = new TPane(fontMD);
panes.add(top);
TPane p = top;
for (int i = 1; i < depth; i++) {
TPane ch = new TPane(fontMD);
p.getChildren().add(ch);
panes.add(ch);
p = ch;
}
scene.setRoot(top);
top.applyCss();
p.getChildren().add(new Pane());
for (TPane pane : panes) {
pane.getStyleClass().setAll("new");
}
count.set(0);
p.getChildren().add(new Pane());
int ct = count.get();
assertTrue(ct <= depth * 4, "Expected<=" + (depth*4) + " observed=" +
ct + " for depth=" + depth);
}
-------------
PR Review Comment: https://git.openjdk.org/jfx/pull/2225#discussion_r3983013119