0

I have a Maven project in IntelliJ IDEA, using JDK 15 and JavaFX 15.

I tried:

  1. Set style for WebEngine
  2. Set blend mode for WebView
  3. Tried WebPage

Nothing succeeded. How do I make the background of the WebView transparent?

My module-info.java:

module project {
    requires javafx.controls;
    requires javafx.web;
    requires javafx.graphics;
    requires uk.co.caprica.vlcj;
    requires uk.co.caprica.vlcj.javafx;

    exports project;
}

My pom.xml dependencies:

<dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj</artifactId>
            <version>4.7.0</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj-javafx</artifactId>
            <version>1.0.2</version>
        </dependency>
</dependencies>

I created a new project and tried to implement the example from the answer. Happened. Unfortunately, the background color does not become transparent after clicking the button. Do you have any idea why the example didn't work?

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;

public class Main extends Application {

    @Override
    public void start(final Stage stage) {
        initStage(stage);
    }

    private void initStage(Stage stage){
        WebView wv = new WebView();
        wv.setStyle("-fx-background-color: transparent;");

        wv.getEngine().load("https://w3schools.com");
        VBox vb = new VBox(wv);
        vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

        Button b = new Button("remove page background");

        b.setOnAction((event) -> {
            HTMLDocumentImpl cast = (HTMLDocumentImpl) wv.getEngine().getDocument();

            try {
                Field f = wv.getEngine().getClass().getDeclaredField("page");
                f.setAccessible(true);
                com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
                page.setBackgroundColor((new java.awt.Color(1, 1, 1, 1)).getRGB());
                f.setAccessible(false);
            } catch (Exception e) {
            }

        });
        vb.getChildren().add(b);

        stage.setScene(new Scene(vb));
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

VM options:

--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.web --add-exports javafx.web/com.sun.webkit.dom=ALL-UNNAMED

enter image description here

Dmitriy
  • 375
  • 1
  • 18

2 Answers2

1

After many attempts, I managed to achieve the desired result.

JDK 15, JavaFX 15.

I tried to implement an example from here: https://stackoverflow.com/a/26519831/10946427

And there was a transparent background. Then I figured out why. This was due to the VM options that I added through a lot of trial and error.

The example won't work without these VM options:

--add-exports
javafx.web / com.sun.javafx.webkit = ALL-UNNAMED
--add-exports
javafx.web / com.sun.webkit = ALL-UNNAMED
--add-opens
javafx.web / javafx.scene.web = ALL-UNNAMED

Most likely the example from the answer, which I choose to be the best, will also work with these options.

Dmitriy
  • 375
  • 1
  • 18
0

The WebView renders the DOM CSS in the web page. If you want a transparent background, you'll have to modify the CSS there as well.

EDIT ... the following works, but opens up a can of worms.

enter image description here

    @Override
    public void start(Stage primaryStage) throws Exception {
    WebView wv = new WebView();
    wv.setStyle("-fx-background-color: transparent;");

    wv.getEngine().load("https://w3schools.com");
    VBox vb = new VBox(wv);
    vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

    Button b = new Button("remove page background");

    b.setOnAction((event) -> {
        HTMLDocumentImpl cast = HTMLDocumentImpl.class.cast(wv.getEngine().getDocument());
        Node item = cast.getElementsByTagName("html").item(0);

        try {
            // Use reflection to retrieve the WebEngine's private 'page' field. 
            Field f = wv.getEngine().getClass().getDeclaredField("page");
            f.setAccessible(true);
            com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
            page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
            f.setAccessible(false);
        } catch (Exception e) {
        }

    });
    vb.getChildren().add(b);

    primaryStage.setScene(new Scene(vb));
    primaryStage.show();
}

added import list

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.w3c.dom.Node;

you may need to add stuff like :

--add-exports=javafx.web/com.sun.webkit.dom=ALL-UNNAMED \

in your run.jvm args parameter of the project.properties file.

note... this is kind of a hack and is not encouraged by oracle.

EDIT (2)

enter image description here

Again, this is kind of a hack and is incomplete at best.

This snapshot is taken after scrolling down immediately after removing the background.

Technically, what I think is going on is that whenever the DOM render engine redraws a frame, it is expected to draw everything hierarchically, and the background is never expected to be transparent. As such it doesn't "clear" stuff and it simply draws the foreground on top of itself repeatedly.

Additionally, there is 1 interesting thing that's happening.

The page background gets remove if you rClick + reloadpage, or if you click the "accept cookies" thing.... which i'm pretty sure triggers a dom update.

So what is evidently going on is that the WebView and WebPage components are part of 2 distinct universes. WebView is DOM manipulation... WebPage is something which the DOM uses during specific phases which deal with rendering.

I'm probably going to get this account suspended if I voice my opinion on why functionality in WebPage is "hidden" ... but from personal experience, whenever things are difficult to get a hold of, such as WebPage, in openJFx, it usually means you'll waste months trying to find a solution for something which will most likely be changed sometime in the future, thereby making your change obsolete.

My advice is to either abandon this problem, or change your mindset entirely.

I think what you should do is essentially emulate the rendering in something like a pane.

so... new Stage... PaneX as Scene, parse whatever is in the webview, and use the css and whatnot to put nodes in the PaneX which essentially look and feel the same as what's in the webview. ... as you can imagine... this will be a pane in the ass...

Alternatively, you might want to consider getting a hold of the openjfx codebase and use modified sources in order to get WebPage to effectively clear itself by adding extra code ... but it's up to you.

Lastly, you have to consider that this is done as is for a reason which probably revolves around performance issues.

Say for example, instead of clearing just the background in the webpage, you remove any and all backgrounds in a CSS in a page, you'll have to do this a lot. which means performance issues since it essentially implies full dom tree parsing at page load.

EverNight
  • 964
  • 7
  • 16
  • The browser page is transparent. WebView cannot be done with transparent background. Do you have a working option? – Dmitriy Feb 19 '21 at 11:21
  • answer to be modified shortly. note this isn't exactly the ok way to do things... but it should at least point you in the right direction. on my machine, this triggers some weird behavior in the DOM ... so use with caution. – EverNight Feb 19 '21 at 12:04
  • Thanks for the answer. I tried to do something like this, but I couldn't, because ... com.sun.webkit.WebPage, HTMLDocumentImpl, com.sun.webkit.WebPage, java.awt.Color - I don't know where to get it. I updated my question and added information about connected plugins in my project. Please see if I am missing something. I tried a lot, but connecting WebPage and others failed. – Dmitriy Feb 19 '21 at 12:32
  • what jdk version are you using? – EverNight Feb 19 '21 at 13:10
  • JDK 15 and JavaFX 15 – Dmitriy Feb 19 '21 at 13:27
  • Hello. I created a new project and tried to implement your example. Happened. Unfortunately, the background color does not become transparent after clicking the button. I have edited my answer. Do you have any idea why the example didn't work? – Dmitriy Feb 22 '21 at 10:48
  • Hi there. 1st of all, if you don't have multiple application instances open, you will get the "accept cookies" dialogue from w3cschools. If you click the button before accepting stuff, it will remove the background. If you click it after accepting the thing, it removes the background once you rClick + reload page. – EverNight Feb 23 '21 at 14:02
  • 2nd. as far as I've seen, this is touching a render engine-level routine which essentially draws pixels based on dom object hierarchy for every DOM frame rendered. This is evident when you scroll once you turn that background transparent. Long story short, since it's not drawing the "background" during a frame update, it maintains the old foreground, and draws new foreground stuff on top of it. (this is evident when you scroll down). – EverNight Feb 23 '21 at 14:09
  • i added some more updates to my answer (at the bottom) – EverNight Feb 23 '21 at 14:18
  • The scrolling problem is most likely solved by making small changes: new java.awt.Color(0, 0, 0, 1)).getRGB()); – Dmitriy Feb 24 '21 at 13:24
  • didn't you say you wanted to make the background transparent? `new java.awt.Color(0, 0, 0, 1)).getRGB());` creates 100% opaque BLACK – EverNight Feb 24 '21 at 13:51
  • I have a transparent background after these changes. Perhaps it becomes 99% transparent – Dmitriy Feb 24 '21 at 14:30
  • I'm curious what you did to get it to work, can you share the code block? – EverNight Feb 24 '21 at 14:46
  • I checked. This does not solve the scrolling problem. This solved my problem, where when I hover over the buttons, they were highlighted in black. After the changes, the selection was no longer black. – Dmitriy Feb 24 '21 at 14:53
  • ah, ok then. If you find a way to deal with the scrolling issue, please let me know – EverNight Feb 24 '21 at 14:58