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

Reply via email to