On Mon, 10 Aug 2026 20:01:24 GMT, Andy Goryachev <[email protected]> wrote:

>> this will only rebuild the style helper that needs it. And only the first 
>> ancestor. I can't see how this could be a problem - do you have a unit test 
>> in mind? 
>> I tested several scenarios and could not spot any problem. Note that this is 
>> a very rare case that usually only happens for the scenarios I implemented 
>> as tests
>
> ok, so here is the test that passes in master and fails spectacularly with 
> this PR:
> 
> 
>     @Test
>     void checkQuadraticPerformace() {
>         scene.getStylesheets().add(toDataURL(
>             """
>             .old {
>                -fx-padding: 1.0;
>             }
>             .new {
>                -fx-padding: 99.0;
>             }
>             """));
> 
>         AtomicInteger counter = new AtomicInteger();
> 
>         class TPane extends Pane {
>             public TPane(String style) {
>                 getStyleClass().add("style");
>             }
> 
>             @Override
>             public Styleable getStyleableParent() {
>                 counter.incrementAndGet();
>                 return super.getStyleableParent();
>             }
>         }
> 
>         int number = 16;
>         ArrayList<TPane> chain = new ArrayList<>();
> 
>         TPane top = new TPane("old");
>         chain.add(top);
> 
>         TPane p = top;
>         for (int i = 1; i < number; i++) {
>             TPane ch = new TPane("old");
>             p.getChildren().add(ch);
>             chain.add(ch);
>             p = ch;
>         }
> 
>         scene.setRoot(top);
>         top.applyCss();
> 
>         counter.set(0);
>         // mark the chain dirty
>         p.getChildren().add(new Pane());
>         int baseline = counter.get();
> 
>         for (TPane pane : chain) {
>             pane.getStyleClass().setAll("new");
>         }
> 
>         counter.set(0);
>         // should not result in quadratic performance
>         p.getChildren().add(new Pane());
>         int newCount = counter.get();
> 
>         assertTrue(newCount <= baseline * 4, () -> {
>             return MessageFormat.format("Baseline={0}, observed={1}", 
> baseline, newCount);
>         });
>     }

Thanks, very helpful! I thought about it for a while, and did come up with 
another implementation that I think is a bit more simple and the other hand 
more optimized for that scenario.

Now I will collect all nodes just once and reuse that list to initialize all 
stale `styleHelper`. That should be much more optimized and I actually think it 
improved the code as well a little bit.
I first thought this may get much more complex, but it isn't IMO.

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/2225#discussion_r3950038048

Reply via email to