Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for QQuickWebEngineView #73

Closed
mchistovib opened this issue Aug 29, 2022 · 34 comments
Closed

Add support for QQuickWebEngineView #73

mchistovib opened this issue Aug 29, 2022 · 34 comments

Comments

@mchistovib
Copy link

mchistovib commented Aug 29, 2022

Is your feature request related to a problem? Please describe.
We are trying to use QQuickWebEngineView in our code, but whenever we try to send that object to java, it always cores the jvm
Describe the solution you'd like
Could you please add support for QQuickWebEngineView? Including all the private stuff, like in this example: https://forum.qt.io/topic/87569/access-webengineview-from-c
we need all signals and methods that it has, incluing but not limited to:

Describe alternatives you've considered
We can't even send it as qobject and use metaobject code like we did for QQuickWindow, because the moment jambi loads WebEngineView it always cores.

Additional context
This will be useful for any developer that would want to use webapps in his qml views. Right now we have to create qwidget just for this webapp, while all the other ui is in qml.

@omix
Copy link
Contributor

omix commented Aug 29, 2022

I’ll see what I can do.
The first thing is to find out why QtJambi cannot convert it to QObject at all. Is QQuickWebEngineView the same as WebEngineView in qml?

@mchistovib
Copy link
Author

mchistovib commented Aug 29, 2022

Is QQuickWebEngineView the same as WebEngineView in qml?

@omix yes it is, but it's not the same as QWebEngineView. It's a kind of a merge between QWebEngineView and QWebEnginePage.

The first thing is to find out why QtJambi cannot convert it to QObject at all

I tried to catch any c++ exceptions to findwhy that crashes, but there were none, it just stops debugging at this point.
I will make a minimal reproducible example, shouldn't be too hard

@mchistovib
Copy link
Author

@omix just run attached files and you will always get jvm crash the moment you click on "click me" button.
CrashRepro.java

package ngMvp;


import io.qt.QtInvokable;
import io.qt.QtUtilities;
import io.qt.core.QCoreApplication;
import io.qt.core.QObject;
import io.qt.core.QUrl;
import io.qt.core.Qt;
import io.qt.qml.QQmlApplicationEngine;
import io.qt.quick.QQuickWindow;
import io.qt.quick.QSGRendererInterface;
import io.qt.webengine.quick.QtWebEngineQuick;
import io.qt.widgets.QApplication;

public class CrashRepro {
    public static void main(String[] args) {
        ///////////// needed for webengine to work ////////////////
        QtWebEngineQuick.initialize();
        QtUtilities.initializePackage("io.qt.webengine.widgets");
        QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts);
        QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGLRhi);
        ///////////////////////////////////////////////////////////
        ///////////////////////// for QT Quick ///////////////////
        QtUtilities.initializePackage("io.qt.quick");
        QtUtilities.loadQtLibrary("QuickControls2");
        //////////////////////////////////////////////////////////
        QApplication.initialize(args);
        UIController controller = new UIController();
        QQmlApplicationEngine engine = new QQmlApplicationEngine();
        engine.rootContext().setContextProperty("controller", controller);

        QUrl mainUrl = new QUrl(ClassLoader.getSystemClassLoader().getResource("ngMvp/mainTest.qml").toExternalForm());

        engine.load(mainUrl);

        QApplication.exec();
        QApplication.shutdown();
    }

    public static class UIController extends QObject {
        @QtInvokable public void passWebView(Object obj) {
            System.out.println("got " + obj.getClass().getName() + ": " + obj);
        }
    }
}

mainTest.qml

import QtQml
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.qmlmodels
import QtWebEngine

Window {
    id: root
    width: 1200
    height: 1000
    visible: true
    title: qsTr("NG-MVP")
    flags: Qt.Window
    color: "black"
    property alias webView: webEngine

    ColumnLayout {
        spacing: 2
        Rectangle {
            height: 100
            width: 1200

            Button {
                text: "click me"
                onClicked: controller.passWebView(webView);
            }
        }

        WebEngineView {
            height: 800
            width: 1200
            id: webEngine
            objectName: "webEngine"
            url: "https://google.com"
        }
    }
}

@mchistovib
Copy link
Author

In debug setup I got more data.
Callstack:

 	KernelBase.dll!RaiseFailFastException�()	Unknown
 	CoreMessaging.dll!Cn::FailFast::Do(struct _EXCEPTION_RECORD *,struct _CONTEXT *)	Unknown
 	CoreMessaging.dll!Cn::FailFast::Do(struct _EXCEPTION_RECORD *)	Unknown
 	CoreMessaging.dll!Cn::FailFast::IndexOutOfRange(void)	Unknown
 	CoreMessaging.dll!Cn::Com::NativeEntry::NoContext_Prologue()	Unknown
 	CoreMessaging.dll!Microsoft::CoreUI::IExportMessageSession$X__ExportAdapter::CloseEndpoint(struct Microsoft::CoreUI::HENDPOINT,enum Microsoft::CoreUI::CloseEndpointFlags$C::Values)	Unknown
 	TextInputFramework.dll!TextInputHostAdapter::Dispose()	Unknown
 	TextInputFramework.dll!TextInputHost::~TextInputHost()	Unknown
 	TextInputFramework.dll!TextInputHost::`vector deleting destructor'(unsigned int)	Unknown
 	TextInputFramework.dll!Microsoft::WRL::Details::RuntimeClassImpl<struct Microsoft::WRL::RuntimeClassFlags<2>,1,0,0,struct IRemoteTextInputHost,struct ITextInputHost,struct IMessageProxyReconnectAdapterOwner,struct IMessageProxyListener,struct ITextInputHostPrivate>::Release(void)	Unknown
 	msctf.dll!CThreadInputMgr::~CThreadInputMgr()	Unknown
 	msctf.dll!CThreadInputMgr::`vector deleting destructor'(unsigned int)	Unknown
 	msctf.dll!CThreadInputMgr::Release()	Unknown
 	Qt6WebEngineCored.dll!Microsoft::WRL::ComPtr<ITfThreadMgr>::InternalRelease() Line 235	C++
 	Qt6WebEngineCored.dll!Microsoft::WRL::ComPtr<ITfThreadMgr>::~ComPtr<ITfThreadMgr>() Line 291	C++
>	Qt6WebEngineCored.dll!ui::`anonymous namespace'::TSFBridgeImpl::~TSFBridgeImpl() Line 183	C++
 	[External Code]	
 	Qt6WebEngineCored.dll!ui::`anonymous namespace'::Finalize(void * data) Line 627	C++
 	Qt6WebEngineCored.dll!`anonymous namespace'::OnThreadExitInternal(`anonymous-namespace'::TlsVectorEntry * tls_data) Line 312	C++
 	Qt6WebEngineCored.dll!base::internal::PlatformThreadLocalStorage::OnThreadExit() Line 347	C++
 	Qt6WebEngineCored.dll!OnThreadExit(void * module, unsigned long reason, void * reserved) Line 67	C++

Exception:
Unhandled exception at 0x00007FFA354EFB62 (KernelBase.dll) in java.exe: 0xC0000602: Произошло исключение Fail Fast. Обработчики исключения не будут вызываться, процесс будет завершен немедленно.

@omix
Copy link
Contributor

omix commented Aug 30, 2022

The callstack looks as if there is an assertion error and the app's crash is caused by the subsequent message dialog. Let me check the example and I will find out what happens. If qml type WebEngineView is actually multiinheriting as you said, then this circumstance could be the cause of the issue.

@omix
Copy link
Contributor

omix commented Aug 30, 2022

new QUrl(ClassLoader.getSystemClassLoader().getResource("ngMvp/mainTest.qml").toExternalForm());

Why don't you use resource url?
new QUrl(":ngMvp/mainTest.qml");
That's not relevant for the issue, just a remark.

@mchistovib
Copy link
Author

I just copied some code from our project :) I suspect it was written this way to support both develop setup and final build from qt jambi deployer

@omix
Copy link
Contributor

omix commented Aug 30, 2022

resource url should also support both situations because it uses classpath.

@omix
Copy link
Contributor

omix commented Aug 30, 2022

I must correct, it is:
new QUrl("qrc:/ngMvp/mainTest.qml");

@omix
Copy link
Contributor

omix commented Aug 30, 2022

I don't see any error. The program returns:

got io.qt.quick.QQuickItem: QQuickWebEngineView(0x27de1ea9ff0, name="webEngine", parent=0x27de1ea92e0, geometry=0,102 1200x800)

That's what I expect.

@mchistovib
Copy link
Author

mchistovib commented Aug 30, 2022

it still crashes every time for me from all computers. @omix do you use 6.2.6 jars from maven with 6.2.5 qt on windows?

@omix
Copy link
Contributor

omix commented Aug 30, 2022

I use my working version of QtJambi but there are no differences to 6.2.6 maven binaries. My Qt is 6.2.4.
What happens if you define passWebView(QQuickItem obj)?

@mchistovib
Copy link
Author

It crashed exactly the same. It doesn't even get inside the method, it never reaches breakpoint with console output

@omix
Copy link
Contributor

omix commented Aug 30, 2022

What happens if you search for webEngine object:

QList<QObject> rootObjects = engine.rootObjects();
QObject webEngine = rootObjects.get(0).findChild("webEngine");

What happens if you dump the object tree:

dump(rootObjects, 0);


private static void dump(QList<QObject> objects, int nestedLevel) {
    	StringBuilder suffix = new StringBuilder();
    	for (int i = 0; i < nestedLevel; i++) {
    		suffix.append("    ");
		}
    	for (QObject obj : objects) {
    		System.out.println(suffix + "| " + obj.getClass().getName() + ": " + obj);
    		dump(obj.children(), nestedLevel+1);
	}
}

In both cases QQuickWebEngineView is represented as QQuickItem java type.

@mchistovib
Copy link
Author

mchistovib commented Aug 30, 2022

What happens if you search for webEngine object:

Crashes on findChild call, just as it did in our real code issue. callstack:

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  io.qt.core.QObject.findChild(Ljava/lang/Class;JLjava/lang/String;I)Lio/qt/core/QObject;+0
j  io.qt.core.QObject.findChild(Ljava/lang/Class;Ljava/lang/String;Lio/qt/core/Qt$FindChildOptions;)Lio/qt/core/QObject;+20
j  io.qt.core.QObject.findChild(Ljava/lang/Class;Ljava/lang/String;)Lio/qt/core/QObject;+20
j  io.qt.core.QObject.findChild(Ljava/lang/String;)Lio/qt/core/QObject;+4
j  ngMvp.CrashRepro$UIController.passWebView()V+18
v  ~StubRoutines::call_stub
j  io.qt.widgets.QApplication.exec()I+0
j  ngMvp.CrashRepro.main([Ljava/lang/String;)V+88
v  ~StubRoutines::call_stub

What happens if you dump the object tree:

crashes the moment it reaches webview:

| io.qt.quick.QQuickWindow: QQuickWindowQmlImpl_QML_3(0x238136cda50 active exposed, visibility=QWindow::Windowed, flags=QFlags<Qt::WindowType>(Window), title="NG-MVP", geometry=8,31 1200x1000) 
    | io.qt.quick.QQuickItem: QQuickRootItem(0x23813881cb0, parent=0x0, geometry=0,0 1200x1000) 
        | io.qt.core.QObject: QQuickDeliveryAgent(0x238133304b0) 
    | io.qt.quick.QQuickItem: QQuickColumnLayout(0x23812f354d0, parent=0x23813881cb0, geometry=0,0 1200x902) 
        | io.qt.quick.QQuickItem: QQuickRectangle(0x238136bd5f0, parent=0x23812f354d0, geometry=0,0 1200x100) 
            | io.qt.quick.QQuickItem: Button_QMLTYPE_1(0x238136bce10, parent=0x238136bd5f0, geometry=0,0 51x24) 
                | io.qt.quick.QQuickItem: QQuickStyleItemButton_QML_2(0x238136c06e0, parent=0x238136bce10, geometry=0,0 51x24) 
                    | io.qt.core.QObject: QQuickBehavior(0x23812f33d60) 
                        | io.qt.core.QObject: QQuickNumberAnimation(0x2381171b850) 
                | io.qt.quick.QQuickItem: QQuickStyleItemButton(0x23813bb3080, parent=0x238136bce10, geometry=0,0 51x24, z=-1) 
                | io.qt.quick.QQuickItem: QQuickIconLabel(0x23813b5e5b0, parent=0x238136bce10, geometry=4,4 43x16) 
                    | io.qt.quick.QQuickItem: QQuickMnemonicLabel(0x238138727f0, name="label", parent=0x23813b5e5b0, geometry=0,0 43x16) 
            | io.qt.core.QObject: QQuickLayoutAttached(0x23813b9ce20) 

call stack

j  io.qt.core.QIterator.value(J)Ljava/lang/Object;+0
j  io.qt.core.QIterator.val()Ljava/lang/Object;+9
j  io.qt.core.QIterator.checkedValue()Ljava/lang/Object;+8
j  io.qt.internal.QtJambiIteratorObject$1.next()Ljava/lang/Object;+44
j  ngMvp.CrashRepro$UIController.dump(Lio/qt/core/QList;I)V+43
j  ngMvp.CrashRepro$UIController.dump(Lio/qt/core/QList;I)V+83
j  ngMvp.CrashRepro$UIController.dump(Lio/qt/core/QList;I)V+83
j  ngMvp.CrashRepro$UIController.passWebView()V+8
v  ~StubRoutines::call_stub
j  io.qt.widgets.QApplication.exec()I+0
j  ngMvp.CrashRepro.main([Ljava/lang/String;)V+88
v  ~StubRoutines::call_stub

Every time it crashes at exactly the same moment - when the webview object is accessed from java. I don't see the point of these tests..

@omix
Copy link
Contributor

omix commented Aug 30, 2022

Does it even happen when you do both operations directoy after engine.load(mainUrl);?

@mchistovib
Copy link
Author

Yes even with this it crashes exactly the same.....

@omix
Copy link
Contributor

omix commented Aug 30, 2022

What happens if you reduce the qml code to the problematic type:

    	QQmlApplicationEngine engine = new QQmlApplicationEngine();
    	engine.loadData(new QByteArray("import QtWebEngine\nWebEngineView{}"));
        QList<QObject> rootObjects = engine.rootObjects();
        QObject webView = rootObjects.get(0);

@mchistovib
Copy link
Author

mchistovib commented Aug 30, 2022

It crashes at rootObjects.get(0)
I don't see the point of repeating the same result over and over. Clearly it's not on my side - it reproduces 100% of the time on all computers that I've tried. Please find some time to test with maven qtjambi jars

@omix
Copy link
Contributor

omix commented Aug 30, 2022

I'll check it with maven natives...

@omix
Copy link
Contributor

omix commented Aug 30, 2022

Yes, 6.2.6 maven natives crash at this position. My latest binaries do not. I will make sure that 6.2.7 is actually fixing it.

@omix
Copy link
Contributor

omix commented Aug 30, 2022

Concerning your request to include QQuickWebEngineView in QtJambi:
This is private API class, i.e. authors of Qt did not intend it to be used in normal C++ code. Therefore, it is also excluded from QtJambi API. Normally, it is for qml programming only. Bringing private API classes to QtJambi makes the maintenance effort a lot bigger. For instance, QQuickWebEngineView itself uses further private API types someone might want to access.

In fact, QQuickWebEngineView's API is similar but not equals to QWebEngineView.
Compare:
https://doc.qt.io/qt-6/qwebengineview.html
https://doc.qt.io/qt-6/qml-qtwebengine-webengineview.html
There is no page()/setPage() no loadProgress(int) signal. Completely all functionality of QQuickWebEngineView can be accessed by meta programming. However, if you don't like to access it in this way, I suggest to program an adapter object in Java transfering all signal calls, method invocations and property access between QML and Java. You would connect the adapter with WebEngineView within QML and then there is no need to access QQuickWebEngineView class in Java.

@mchistovib
Copy link
Author

As long as we would still be able to use all it's signals/properties using meta object, we can work with that. Unfortuanately I can't check that while it's crashing :)

@mchistovib
Copy link
Author

@omix is it possible to at least add https://doc.qt.io/qt-6/qml-qtwebengine-webenginesettings.html ? I assume it's a private unavailable object too now, but its fields are all bools/ints/etc and just one enum from the same object

@omix
Copy link
Contributor

omix commented Aug 30, 2022

QQuickWebEngineSettings inherits QObject. You can access all these properties by QObject.property(key) and QObject.setProperty(key, value).

@mchistovib
Copy link
Author

Yeah I know, it would just be better for code to have thisese fields in java classes with compile checks instead of using some string constants.
Would it make sense to add such wrapper to jambi itself btw? I don't think we would be the only ones using it

@omix
Copy link
Contributor

omix commented Aug 31, 2022

QQuickWebEngineSettings is a QObject-wrapped QWebEngineSettings. I'm thinking about a converter function.

@omix
Copy link
Contributor

omix commented Aug 31, 2022

This is the final solution I can offer:

QWebEngineSettings settings = QtWebEngineQuick.toWebEngineSettings((QObject)webEngineView.property("settings"));
QWebEngineScriptCollection userScripts = QtWebEngineQuick.toWebEngineScriptCollection((QObject)webEngineView.property("userScripts"));

@mchistovib
Copy link
Author

looks great, thanks!

@mchistovib
Copy link
Author

@omix do you have any estimates for the next release?

@omix
Copy link
Contributor

omix commented Sep 1, 2022

I am busy with releasing this entire day. Tomorrow, everything should be online and accessible.

@omix
Copy link
Contributor

omix commented Sep 1, 2022

I hope the system does not treat me again as malicious attack as it did last time.

@mchistovib
Copy link
Author

mchistovib commented Sep 1, 2022

I am busy with releasing this entire day

Did you forget to push git changes to this repo? :) Last commit is 13 days ago, I doubt you are releasing on it

@omix
Copy link
Contributor

omix commented Sep 1, 2022

I will push after uploading the binaries.

omix added a commit that referenced this issue Sep 2, 2022
Bugfix Issue #73
Bugfix Issue #72
Bugfix Issue #71
@omix omix closed this as completed Sep 2, 2022
omix added a commit that referenced this issue Jul 25, 2023
Bugfix Issue #73
Bugfix Issue #72
Bugfix Issue #71
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants