Skip to content

OSR: element with a running CSS animation keeps its last painted state after navigating away #548

Description

@timoreichen

Describe the bug

In OSR mode, an element that has a running CSS animation keeps its last painted state on
screen after the browser navigates to another page. The new page paints everywhere except that
element's bounding box, which keeps showing the old page indefinitely.

The stale area is exactly the animated element's rectangle: move the animation up the page and
the artifact moves with it.

To Reproduce

  1. Save a.html — a page with one animated element:
<!doctype html><html><head><meta charset="utf-8"><style>
html,body{margin:0;padding:0;background:#101020;height:300vh}
.hero{position:absolute;top:75vh;left:5%;width:90%;height:60vh;background:#dbdfe4;
      border-radius:12px;animation:pulse 2s infinite}
@keyframes pulse{0%{opacity:1}50%{opacity:.85}100%{opacity:1}}
</style></head><body><div class="hero"></div></body></html>
  1. Save green.html — a plain solid-colour page, so the artifact is obvious:
<!doctype html><html><head><meta charset="utf-8"><style>
html,body{margin:0;padding:0;height:100%;background:#00c000}
</style></head><body></body></html>
  1. Run the attached reproducer with -Da=file:///.../a.html -Db=file:///.../green.html. It
    creates an OSR browser on a.html, waits 10 s, then calls loadURL for green.html.

  2. Look at the window.

Expected behavior

The whole view shows the green page.

Instead, the rectangle where .hero was stays #dbdfe4, including its border-radius. It
does not repaint. Measured as a fraction of the lower part of the window that is actually
green: 19.9 % with the animation, 100 % without it.

Versions

  • OS: macOS 27.0 (26A428), arm64
  • Java Version: OpenJDK 25.0.2 (BellSoft Liberica)
  • JCEF Version: d3de827
  • CEF Version: 146.0.10+g8219561+chromium-146.0.7680.179

Built with cmake -G Xcode -DPROJECT_ARCH=arm64 + xcodebuild -configuration Release, no
source changes, and run through the produced jcef_app.app with its stock JavaAppLauncher
and stock JVMOptions — only JVMMainClassName was pointed at the reproducer. No extra CEF
command-line switches.

Additional context

Reproducer — SwingStaleBandRepro.java
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

/**
 * Swing/JCEF counterpart of the JavaFX reproducer: loads page A (which has a running CSS
 * animation), navigates to a plain solid-colour page B, and leaves the window up.
 *
 * Off-screen rendering, to match the CEFFX case.
 */
public class SwingStaleBandRepro {

    private static final String A = System.getProperty("a");
    private static final String B = System.getProperty("b");

    public static void main(String[] args) {
        if (!CefApp.startup(args)) {
            System.out.println("### startup failed");
            return;
        }
        CefSettings settings = new CefSettings();
        settings.windowless_rendering_enabled = true;   // OSR, as in the CEFFX report

        CefApp app = CefApp.getInstance(settings);
        CefClient client = app.createClient();
        CefBrowser browser = client.createBrowser(A, true /* OSR */, false);
        Component ui = browser.getUIComponent();

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("SWING-REPRO");
            JPanel content = new JPanel(new BorderLayout());
            content.setPreferredSize(new Dimension(900, 700));
            content.add(ui, BorderLayout.CENTER);
            frame.setContentPane(content);
            frame.pack();
            frame.setLocation(60, 60);
            frame.setVisible(true);

            // Same timeline as the JavaFX reproducer: let A render and animate, then navigate.
            new Timer(10_000, e -> {
                ((Timer) e.getSource()).stop();
                System.out.println("### navigating to " + B);
                browser.loadURL(B);
                new Timer(15_000, e2 -> {
                    ((Timer) e2.getSource()).stop();
                    System.out.println("### READY");
                    System.out.flush();
                }) {{ setRepeats(false); }}.start();
            }) {{ setRepeats(false); }}.start();
        });
    }
}

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions