JDK-8208622 : [WebView] IllegalStateException when invoking print API with html form controls
  • Type: Bug
  • Component: javafx
  • Sub-Component: web
  • Affected Version: 8u171,9,10,openjfx11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: x86_64
  • Submitted: 2018-07-31
  • Updated: 2020-01-31
  • Resolved: 2018-08-07
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 Other
8u191Fixed openjfx11Fixed
Description
ADDITIONAL SYSTEM INFORMATION :
OS: Ubuntu 14.04 / 18.04 / Windows 10 Pro 1803
Java: Java 8 Update 172/181

A DESCRIPTION OF THE PROBLEM :
When I print WebView by PrinterJob.printPage(Node), IllegalStateException is thrown. the page is printed omitting checkboxes. Printing by WebEngine.print(PrinterJob) results in the same.
A similar problem is reported in 
https://stackoverflow.com/questions/50476171/illegalstateexeption-when-using-javafx-webengine-print-function

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Load a HTML page by WebView in JavaFX. The HTML contains checkboxes.
Print the WebView by PrinterJob.printPage(Node).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The WebView is printed as shown on the screen.
ACTUAL -
The WebView is printed, but checkboxes is not printed. And IllegalStateException is thrown as follows: 

Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: the method should not be called in this context
	at com.sun.javafx.webkit.theme.RenderThemeImpl.ensureNotDefault(RenderThemeImpl.java:250)
	at com.sun.javafx.webkit.theme.RenderThemeImpl.createWidget(RenderThemeImpl.java:263)
	at com.sun.webkit.WebPage.twkUpdateContent(Native Method)
	at com.sun.webkit.WebPage.lambda$print$40(WebPage.java:666)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.gtk.GtkApplication.enterNestedEventLoopImpl(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication._enterNestedEventLoop(GtkApplication.java:211)
	at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
	at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
	at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:590)
	at com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.implPrintPage(J2DPrinterJob.java:1146)
	at com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.access$500(J2DPrinterJob.java:972)
	at com.sun.prism.j2d.print.J2DPrinterJob.print(J2DPrinterJob.java:797)
	at javafx.print.PrinterJob.printPage(PrinterJob.java:398)
	at javafx.print.PrinterJob.printPage(PrinterJob.java:414)
	at bugreport.Main.print(Main.java:67)
	at bugreport.Main.lambda$2(Main.java:45)
	at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
	at java.lang.Thread.run(Thread.java:748)
Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: the method should not be called in this context
	at com.sun.javafx.webkit.theme.RenderThemeImpl.ensureNotDefault(RenderThemeImpl.java:250)
	at com.sun.javafx.webkit.theme.RenderThemeImpl.createWidget(RenderThemeImpl.java:263)
	at com.sun.webkit.WebPage.twkUpdateContent(Native Method)
	at com.sun.webkit.WebPage.lambda$print$40(WebPage.java:666)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.gtk.GtkApplication.enterNestedEventLoopImpl(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication._enterNestedEventLoop(GtkApplication.java:211)
	at com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:511)
	at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)
	at com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:590)
	at com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.implPrintPage(J2DPrinterJob.java:1146)
	at com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.access$500(J2DPrinterJob.java:972)
	at com.sun.prism.j2d.print.J2DPrinterJob.print(J2DPrinterJob.java:797)
	at javafx.print.PrinterJob.printPage(PrinterJob.java:398)
	at javafx.print.PrinterJob.printPage(PrinterJob.java:414)
	at bugreport.Main.print(Main.java:67)
	at bugreport.Main.lambda$2(Main.java:45)
	at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
	at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
	at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
	at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
	at java.lang.Thread.run(Thread.java:748)


---------- BEGIN SOURCE ----------
------Main.java----------------
package bugreport;
	
import java.util.concurrent.CountDownLatch;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Worker.State;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;


public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		try {
			WebView root = new WebView();
			WebEngine engine = root.getEngine();
			engine.load(getClass().getResource("BugReport.html").toString());
			
			CountDownLatch latch = new CountDownLatch(1);
			ChangeListener<State> loadFinish = (observable, oldValue, newValue) -> {
				if (newValue == State.SUCCEEDED) {
					latch.countDown();
				}
			};
			engine.getLoadWorker().stateProperty().addListener(loadFinish);
			
			Scene scene = new Scene(root,400,400);
			primaryStage.setScene(scene);
			primaryStage.show();
			
			new Thread(() -> {
				// wait the html loaded.
				try {
					latch.await();
				} catch (InterruptedException e) {
				}				
				Platform.runLater(() -> {
					print(root);
					/*
					PrinterJob job = PrinterJob.createPrinterJob(Printer.getDefaultPrinter());
					engine.print(job);
					job.endJob();
					*/					
				});
			}).start();

		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
	
	private static void print(WebView node) {
		Printer printer = Printer.getDefaultPrinter();
		if (printer != null) {
			PrinterJob job = PrinterJob.createPrinterJob(printer);
			try {
				job.printPage(node);
			} finally {
				job.endJob();
			}
		}
	}
}
--------End of Main.java-----------------------------------------------------------
-----------BugReport.html----------------------------------------
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>BugReport</title>
  </head>
  <body><input name="bugreport" type="checkbox">
    <p>This sentence is printed.<br>
    </p>
  </body>
</html>
---------End of BugReport.html--------------------
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
In my case, no exception is thrown when checkboxes are removed from the HTML.
But the checkboxes are not printed, of course.

FREQUENCY : always



Comments
Changeset: 94d23e05b3de Author: arajkumar Date: 2018-08-07 12:25 +0530 URL: http://hg.openjdk.java.net/openjfx/jfx-dev/rt/rev/94d23e05b3de 8208622: [WebView] IllegalStateException when invoking print API with html form controls Reviewed-by: kcr, mbilla
07-08-2018

+1
04-08-2018

+1
03-08-2018

Thanks [~kcr]. Addressed the review comments in `webrev.02`. (Also updated Copyright year). http://cr.openjdk.java.net/~arajkumar/8208622/webrev.02
03-08-2018

The code and test look good. One comment: It might be better to also restore the error stream back to ERR right before you access the print stream in addition to restoring it in the @After method. It doesn't seem to matter for this case, but as a general practice, if some code were to try to print anything before the @After method was called, it wouldn't show up, but would go to the ByteArrayOutputStream; junit doesn't seem to do this even when an assertion error is thrown, but I don't know whether we should rely on that.
02-08-2018

Addressed review comments in webrev.01. http://cr.openjdk.java.net/~arajkumar/8208622/webrev.01 In addition to that, added html form controls rendering test as well.
02-08-2018

Root cause: `RenderThemeJava` instance is associated with form controls rendering in `WebPage::prePaint` and disassociated in `WebPage::postPaint`. However, when printing is invoked, `WebPage.print` directly invokes `WebPage::paint` without invoking `WebPage::prePaint`, which causes the exception because there is no valid `RenderThemeJava` instance is available to render form controls. Proposed solution: Associate java `RenderTheme` instance with `PlatformContextJava` and make use of it while rendering FormControls. This solution also eliminates global state. Webrev: http://cr.openjdk.java.net/~arajkumar/8208622/webrev EDIT-1: If you want to make use of user provided test(WebviewPrintTest.java), add ` job.showPrintDialog(stage);` and select 'Open in Preview' to avoid printing it to physical paper.
02-08-2018

I took a look at the code changes and they seem OK, but haven't tested it yet. Quick comment on the test: I recommend to wrap the following in a try/finally, with System.setErr in the finally block, in case either method throws an exception so that the error stream is restored (or use an "@After" method to restore it) : 76 loadContent(String.format("<body>%s</body>", html)); 77 submit(r); 78 System.setErr(ERR);
02-08-2018

There are 2 ways to print the web page, 1. PrinterJob.printPage(node) 2. WebEngine.print(printerJob) The reported issue is related to #1, #2, `WebEngine.print` also fails with following exception, which need to be handled too. Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: the method should not be called in this context at javafx.web/com.sun.javafx.webkit.theme.RenderThemeImpl.ensureNotDefault(RenderThemeImpl.java:249) at javafx.web/com.sun.javafx.webkit.theme.RenderThemeImpl.createWidget(RenderThemeImpl.java:262) at javafx.web/com.sun.webkit.WebPage.twkPrint(Native Method) at javafx.web/com.sun.webkit.WebPage.lambda$print$7(WebPage.java:1853) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427) at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoopImpl(Native Method) at javafx.graphics/com.sun.glass.ui.mac.MacApplication._enterNestedEventLoop(MacApplication.java:109) at javafx.graphics/com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:509) at javafx.graphics/com.sun.glass.ui.EventLoop.enter(EventLoop.java:107) at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:591) at javafx.graphics/com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.implPrintPage(J2DPrinterJob.java:1178) at javafx.graphics/com.sun.prism.j2d.print.J2DPrinterJob$J2DPageable.access$500(J2DPrinterJob.java:1004) at javafx.graphics/com.sun.prism.j2d.print.J2DPrinterJob.print(J2DPrinterJob.java:829) at javafx.graphics/javafx.print.PrinterJob.printPage(PrinterJob.java:399) at javafx.graphics/javafx.print.PrinterJob.printPage(PrinterJob.java:415) at javafx.web/javafx.scene.web.WebEngine.print(WebEngine.java:1613) at bugreport.FormControlPrint.print(FormControlPrint.java:68) at bugreport.FormControlPrint.lambda$start$1(FormControlPrint.java:44) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427) at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
01-08-2018

Root cause: `RenderThemeJava` instance is associated with form controls rendering in `WebPage::prePaint` and disassociated in `WebPage::postPaint`. However, when printing is invoked, `WebPage.print` directly invokes `WebPage::paint` without invoking `WebPage::prePaint`, which causes the exception because there is no valid `RenderThemeJava` instance is available to render form controls.
01-08-2018

The solution which I proposed is not an optimal one.
01-08-2018

I had fixed a similar issue in Accelerated Composition enhancement[1], actually the `RenderThemeJava` instance which we create at `WebPage::prePaint` must be destroyed after finishing `WebPage::postPaint`. diff -r 3527ff907f43 modules/javafx.web/src/main/native/Source/WebCore/platform/java/WebPage.cpp --- a/modules/javafx.web/src/main/native/Source/WebCore/platform/java/WebPage.cpp Mon Jul 30 18:27:52 2018 +0200 +++ b/modules/javafx.web/src/main/native/Source/WebCore/platform/java/WebPage.cpp Wed Aug 01 11:56:28 2018 +0530 @@ -312,7 +312,6 @@ void WebPage::postPaint(jobject rq, jint x, jint y, jint w, jint h) { - RenderThemeJava::setTheme(nullptr); if (!m_page->inspectorController().highlightedNode() #if USE(ACCELERATED_COMPOSITING) && !m_rootLayer @@ -342,6 +341,7 @@ } gc.platformContext()->rq().flushBuffer(); + RenderThemeJava::setTheme(nullptr); } [1] https://github.com/javafxports/openjdk-jfx/pull/51/files#diff-c7d3c413f0e0b0260b8b65c75cf8eec8R329
01-08-2018

Issue is reproducible in JDK 8u181, latest OpenJFX and its a regression in JDK 8u171. Windows 10, 64-bit JDK results --------------------------- 8u60 : Pass 8u162-b11 : Pass 8u171-b11 : Fail <-- regression 8u181 : Fail 11-ea+13 : Fail latest openjfx : Fail When html page is printed from webview having input type 'checkbox', html page is printed without checkbox with following exception: java.lang.IllegalStateException: the method should not be called in this context at javafx.web/com.sun.javafx.webkit.theme.RenderThemeImpl.ensureNotDefault(RenderThemeImpl.java:249) When input type is changed to 'text' type, html page is printed with text field but again with same exception.
01-08-2018