Skip to content

8389092: PopupWindow.show() unconditionally overwrites user-set scene stylesheets with the owner's stylesheets - #2236

Open
Maran23 wants to merge 2 commits into
openjdk:masterfrom
Maran23:8389092-PopupWindow.show()-unconditionally-overwrites-user-set-scene-stylesheets-with-the-owner's-stylesheets
Open

8389092: PopupWindow.show() unconditionally overwrites user-set scene stylesheets with the owner's stylesheets#2236
Maran23 wants to merge 2 commits into
openjdk:masterfrom
Maran23:8389092-PopupWindow.show()-unconditionally-overwrites-user-set-scene-stylesheets-with-the-owner's-stylesheets

Conversation

@Maran23

@Maran23 Maran23 commented Jul 31, 2026

Copy link
Copy Markdown
Member

PopupWindow will always overwrite its Scene (user agent) stylesheets when an owner was set and show is called.
Code like this:

        final Popup popup = new Popup();
        popup.getScene().setUserAgentStylesheet(popupUserAgentStylesheet);
        popup.getScene().getStylesheets().add(popupStylesheet);

will do nothing, because your added stylesheets will be later overwritten when show is called.
Andy and I were already wondering about this behavior two years ago: #1394 (comment)


I can't see any reason why we should do that. Instead, this PR will only add the stylesheets of the owner if they do not exist already.
Additionally, we will not overwrite the user agent stylesheet if it was already set.

Added tests for all combinations I can think of. This PR also fixes NPEs that can happen when the owner window has no 'root window'.

There is already a test for the Cursor behavior, but there was none that verifies that the Cursor is not overwritten, so added one as well.

if (sceneValue.getCursor() == null) {
sceneValue.setCursor(ownerScene.getCursor());
}

Now, we will never overwrite anything that the developer set (before showing). And as we can see above, this was already done this way with the Cursor.



Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)

Issue

  • JDK-8389092: PopupWindow.show() unconditionally overwrites user-set scene stylesheets with the owner's stylesheets (Bug - P3)

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/2236/head:pull/2236
$ git checkout pull/2236

Update a local copy of the PR:
$ git checkout pull/2236
$ git pull https://git.openjdk.org/jfx.git pull/2236/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 2236

View PR using the GUI difftool:
$ git pr show -t 2236

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/2236.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper

bridgekeeper Bot commented Jul 31, 2026

Copy link
Copy Markdown

👋 Welcome back mhanl! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk

openjdk Bot commented Jul 31, 2026

Copy link
Copy Markdown

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk Bot added the rfr Ready for review label Jul 31, 2026
@openjdk

openjdk Bot commented Jul 31, 2026

Copy link
Copy Markdown

The total number of required reviews for this PR has been set to 2 based on the presence of this label: rfr. This can be overridden with the /reviewers command.

@mlbridge

mlbridge Bot commented Jul 31, 2026

Copy link
Copy Markdown

Webrevs


private void showImpl(final Window owner) {
Window rootWindow = getRootWindow(owner);
if (rootWindow == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not equivalent change: the old code set this.ownerWindow (to null in this case, see L467)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a point but I don't see how this could be a problem. I can't imagine a usecase where you want to call show just to reset the owner

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was pointing out that the change is not equivalent - in the old code, a null owner causes this.ownerWindow be set to null, in the new code it does not.

private void showImpl(final Window owner) {
Window rootWindow = getRootWindow(owner);
if (rootWindow == null) {
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just a bit confused here. Referring to old version' line numbers, it looks like we should not have a null value returned from getRootWindow(owner): it's being dereferenced in L477 and also L485.

void applyStylesheetFromOwner(Window owner) {
// JDK-8116444
Window rootWindow = getRootWindow(owner);
if (rootWindow == null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing - is it even possible for the rootWindow to be null?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as you can see in the tests I wrote.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a question about the tests you wrote.
Sorry, I need to spell my question out. Looking at (old) L502:

final Scene ownerScene = getRootWindow(owner).getScene();

you see that getScene() dereferences the root window, so it can't be null there, I guess, otherwise we had seen an NPE.

List<String> newStylesheets = new ArrayList<>();
for (String stylesheet : ownerScene.getStylesheets()) {
if (!scene.getStylesheets().contains(stylesheet)) {
newStylesheets.add(stylesheet);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can a PopupWindow be reused? if so, the stylesheets might accumulate from the earlier cycles. is this ok? should we keep track of the stylesheets added here and remove them on hide()?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not happen as we will check with contains. There is also a test for this scenario.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I need to spell this out. L527 adds new stylesheets, but there is not removing operation. So if you keep adding stylesheets and not removing the old stylesheets, they just get accumulated.

Think of a scenario when a stylesheet is not sourced from a file/resource, but generated programmatically via a data url.

if (ownerScene.getUserAgentStylesheet() != null) {
scene.setUserAgentStylesheet(ownerScene.getUserAgentStylesheet());
if (scene.getUserAgentStylesheet() == null && ownerScene.getUserAgentStylesheet() != null) {
scene.setUserAgentStylesheet(ownerScene.getUserAgentStylesheet());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there might be another issue:

  • owner A has the user agent stylesheet A.css
  • a popup with no stylesheet is shown with the owner A. A.css is shown
  • the popup gets hidden
  • either owner A changes the stylesheet to B.css, or the popup is reusing with a different owner
  • the popup gets shown

since scene.getUserAgentStylesheet() is already A.css, this code does not set the new stylesheet, leaving the popup with a wrong style.

I know reusing the popup (context menu, etc.) is a bad idea, but surprisingly, I saw this happen many times in the past.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, there is no way that you can actually change the user agent stylesheet. It is read once and then cached.
See e.g. #525, where a user proposed to change that.

So I think we are actually good here

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the case for the PopupTest that works in master and fails with this fix:

    @Test
    public void updateStylesheetFromOwnerStage() {
        String cssA = toBase64(".root { -fx-fill: green; }");
        String cssB = toBase64(".root { -fx-fill: red; }");
        scene.setUserAgentStylesheet(cssA);

        Popup p = new Popup();
        p.show(stage);
        assertEquals(cssA, p.getScene().getUserAgentStylesheet());

        p.hide();
        scene.setUserAgentStylesheet(cssB);
        p.show(stage);
        assertEquals(cssB, p.getScene().getUserAgentStylesheet());
    }

assertEquals(List.of(sharedStylesheet), popup.getScene().getStylesheets());
}

private String toBase64(String stylesheet) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably be static...
and since we are using it more than once, maybe we can move it to a new graphics-specific Utils class?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rfr Ready for review

Development

Successfully merging this pull request may close these issues.

2 participants