JDK-8150982 : Crash when calling WebEngine.print on background thread
  • Type: Bug
  • Component: javafx
  • Sub-Component: web
  • Affected Version: 8,9
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2016-03-01
  • Updated: 2017-09-07
  • Resolved: 2016-09-29
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u131Fixed 9Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
java version "1.8.0_75"
Java(TM) SE Runtime Environment (build 1.8.0_75-b09)
Java HotSpot(TM) Client VM (build 25.75-b09, mixed mode, sharing)


Does this problem occur on J2SE 6ux or 7ux or 8ux?  Yes / No (pick one)

Yes, earlier versions of Java 8

Operating System Configuration Information (be specific):
Microsoft Windows [Version 6.1.7601]


Hardware Configuration Information (be specific):
HP EliteBook 8760W
Windows 7 Professional 64-bit (SP1)
8G Ram

Bug Description:

This SVG document shows fine in the webview, but does not print correctly.  

Sometimes it causes the VM to crash, other times it will go into an infinite loop of exceptions.

This document is a bit weird, but it shows find in the webview, so I would think it would print OK 
as well.  Some SVG documents I try print fine, others behave as in this example.

Exception in thread "JavaFX Application Thread"
java.lang.NullPointerException
        at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
        at com.sun.webkit.Timer.fireTimerEvent(Unknown Source)
        at com.sun.webkit.Timer.notifyTick(Unknown Source)
        at javafx.scene.web.WebEngine$PulseTimer.lambda$static$45(Unknown Source)
        at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.Toolkit.runPulse(Unknown Source)
        at com.sun.javafx.tk.Toolkit.firePulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
        at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
        at com.sun.webkit.Timer.fireTimerEvent(Unknown Source)
        at com.sun.webkit.Timer.notifyTick(Unknown Source)
        at javafx.scene.web.WebEngine$PulseTimer.lambda$static$45(Unknown Source)
        at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.Toolkit.runPulse(Unknown Source)
        at com.sun.javafx.tk.Toolkit.firePulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
        at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
        at com.sun.webkit.Timer.fireTimerEvent(Unknown Source)
        at com.sun.webkit.Timer.notifyTick(Unknown Source)
        at javafx.scene.web.WebEngine$PulseTimer.lambda$static$45(Unknown Source)
        at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.Toolkit.runPulse(Unknown Source)
        at com.sun.javafx.tk.Toolkit.firePulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(UnknownSource)
        at java.lang.Thread.run(Unknown Source) 
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
        at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
        at com.sun.webkit.Timer.fireTimerEvent(Unknown Source)
        at com.sun.webkit.Timer.notifyTick(Unknown Source)
        at javafx.scene.web.WebEngine$PulseTimer.lambda$static$45(Unknown Source)
        at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.Toolkit.runPulse(Unknown Source)
        at com.sun.javafx.tk.Toolkit.firePulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source)
        at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(Unknown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
        at java.lang.Thread.run(Unknown Source)


Steps to Reproduce (be specific):

Run the program and try to print. 
Comments
Removed 8-bpr-approved label, as this fix did not ship as a BPR, since the issue was corrected by the full webkit backport - handled in Oct release.
13-01-2017

Thanks for the review. In case you are interested, the 'page' argument was added in 9 as part of the impl_ encapsulation. The Printable class moved from being an inner class of WebEngine, where it could access its containing class' fields directly, into a com.sun.* package.
29-09-2016

Changeset: f5bdcd0e4b64 Author: kcr Date: 2016-09-29 05:57 -0700 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/f5bdcd0e4b64 8150982: Crash when calling WebEngine.print on background thread 8165098: WebEngine.print will attempt to print even if the printer job is complete or has an error Summary: Perform beginPrinting and endPrinting on correct thread; check for errors in print method Reviewed-by: prr, ghb
29-09-2016

+1, Looks good to me.
29-09-2016

It seems to me that maybe the reason it did not apply cleanly is that the "page" argument is not there on 8. It seems like this represents a native webkit instance and that was somehow implied on 8 but is disentangled on 9. Anyway "+1"
28-09-2016

For completeness, here is the 8u webrev: http://cr.openjdk.java.net/~kcr/8150982/webrev-8u.00/ I post it here because the 9 patch did not apply 100% cleanly. The only difference in the 8u webrev is to account for a difference in the 9 versus 8u WebEngine.java files on one line (the Printable constructor) in the block which is indented by the fix due to the 'if' check. Here is a diff of the two patches: for (int i = 0; i < pageCount; i++) { -- Node printable = new Printable(page, i, width); +- Node printable = new Printable(i, width); - job.printPage(printable); + if (printStatusOK(job)) { -+ Node printable = new Printable(page, i, width); ++ Node printable = new Printable(i, width); + job.printPage(printable); + } }
28-09-2016

+1
28-09-2016

Webrev: http://cr.openjdk.java.net/~kcr/8150982/webrev.00/ As indicated above, the root cause of this bug is that the WebPage.beginPrinting and WebPage.endPrinting methods, which are used by WebEngine.print to prepare the page for printing and the finish the printing, respectively, are not thread-safe. The WebPage.print method iteself correctly uses the invokeOnEventThread method to print each page on the correct thread. The fix is to also use invokeOnEventThread in beginPrinting and endPrinting. While debugging this, we discovered a related problem in that WebEngine.print will prepare for printing and loop through the pages in the WebEngine calling print on each of them even if there is an error. This needlessly ties up the FX application thread doing throw-away work and was partially responsible for JDK-8165098. The proposed fix addresses this by returning from the method if the job status is in error when called, and by skipping any remaining pages if an error occurs during the printing of multiple pages, as suggested by Phil in the bug report for JDK-8165098.
28-09-2016

Workaround: the application can call WebEngine.print on the JavaFX Application thread using Platform.runLater
01-09-2016

The root cause of the crash is that the implementation of the WebEngine.print method is not thread-safe if called off of the FX Application Thread. At least not for a live scene, and quite possibly not at all. Specifically, the beginPrint and endPrint methods in WebPage call WebKit native code -- twkBeginPrinting and twkBeginPrinting, respectively -- that expects to be called in the event thread (the FX app thread). The WebPage.print method itself correctly wraps the call to twkPrint in a runLater (using Invoker.invokeOnEventThread which runs the runnable directly if already on the event thread). It seems that the same treatment is needed in beginPrint and endPrint when calling twkBeginPrinting and twkBeginPrinting. I tested the following fix and was unable to reproduce the crash with a test application. More testing is needed. @@ -1761,7 +1764,23 @@ log.warning("beginPrinting() called for a disposed web page."); return 0; } - return twkBeginPrinting(getPage(), width, height); + AtomicReference<Integer> retVal = new AtomicReference<>(0); + final CountDownLatch l = new CountDownLatch(1); + Invoker.getInvoker().invokeOnEventThread(() -> { + try { + int nPages = twkBeginPrinting(getPage(), width, height); + retVal.set(nPages); + } finally { + l.countDown(); + } + }); + + try { + l.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return retVal.get(); } finally { unlockPage(); } @@ -1774,7 +1793,20 @@ log.warning("endPrinting() called for a disposed web page."); return; } - twkEndPrinting(getPage()); + final CountDownLatch l = new CountDownLatch(1); + Invoker.getInvoker().invokeOnEventThread(() -> { + try { + twkEndPrinting(getPage()); + } finally { + l.countDown(); + } + }); + + try { + l.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } finally { unlockPage(); }
01-09-2016

There is no formal means to get any such information ERROR can be any number of things - (1) "misuse: of the API, (2) an exception thrown from some FX rendering code that is caught by printing (this would be revealed by the prism.debug property), or (3) perhaps some problem talking to GDI/Windows printing APIs. In this case I believe the ERROR is as a result of trying to print another page once the requested page range has already been reached. So something like #1 is the closest.
01-09-2016

CAP member has tested 8u112 b10, but it still does not work for them.
26-08-2016

It is a showstopper issue for the CAP member. They would like have the fix by July, so they could roll out their JavaFX code after the July release.
24-03-2016

Seems not a critical for the release. OK to defer it.
11-03-2016

Defer - justification: No cycles to resolve before GAC, we will fix ASAP, and BPR if critical before July.
02-03-2016