1

After understanding how to integrate JavaScript functions into Qt-QML I am now trying to apply what I have learned in my previous post to a small real world example in my lab.

I am trying to access to my robots via wi-fi using using a third party device present in my lab.

I have a log-in form provided by the device as shown in the print screen given below. The address is, for example, https://123.456.789.123:7878 (which I made up but it is for understanding the idea):

log-in

After navigating the html page using the inspector tool I arrived to the log in button class as shown below:

inspector-tool

The problem I have is that I am now trying to autofill the password of the log-in and automatically access it. After logging in I should be able to see the page below, but I don't.

after-logging-in

I should mention that I reused the Quick Nanao Browser Example beacause it does work well when there is need to by-pass a windows that requires certification.

The example

I worked a lot on the Quick Nanao Browser Example to make it much more short and focusing exactly where the problem is and how I am trying to solve it.

The code snippet I am using is the one below:

main.cpp

#include <QtQml/QQmlApplicationEngine>
#include <QtQml/QQmlContext>
#include <QtWebEngine/qtwebengineglobal.h>
static QUrl startupUrl()
{
    QUrl ret;
    QStringList args(qApp->arguments());
    args.takeFirst();
    for (const QString &arg : qAsConst(args)) {
        if (arg.startsWith(QLatin1Char('-')))
             continue;
        ret = Utils::fromUserInput(arg);
        if (ret.isValid())
            return ret;
    }
    return QUrl(QStringLiteral("https://123.456.789.123:7878"));
}

int main(int argc, char **argv)
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QtWebEngine::initialize();
    Application app(argc, argv);
    QQmlApplicationEngine appEngine;
    Utils utils;
    appEngine.rootContext()->setContextProperty("utils", &utils);
    appEngine.load(QUrl("qrc:/ApplicationRoot.qml"));
    if (!appEngine.rootObjects().isEmpty())
        QMetaObject::invokeMethod(appEngine.rootObjects().first(), "load", Q_ARG(QVariant, startupUrl()));
    else
        qFatal("Failed to load sources");
    return app.exec();
}

ApplicationRoot.qml

import QtQuick 2.1
import QtWebEngine 1.9
QtObject {
    id: root
    property string script_password: "
        setTimeout(function(){
            var input_password = document.getElementsByName('password')[0];
            input_password.value = '%1';
            var button = document.getElementById('passwordNext');
            button.click();
            }, 100);
            ".arg(password)

    property QtObject defaultProfile: WebEngineProfile {}
    property QtObject otrProfile: WebEngineProfile {
        offTheRecord: true
    }
    property Component browserWindowComponent: BrowserWindow {
        applicationRoot: root
        onClosing: destroy()
    }    
    function createWindow(profile) {...}
    function createDialog(profile) {...}
    function load(url) {...}
}

BrowserWindow.qml

import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.0
import QtQuick.Window 2.1
import QtWebEngine 1.9

ApplicationWindow {
    id: browserWindow

    property string password: "abcdefghilmno"

    property QtObject applicationRoot
    property Item currentWebView: tabs.currentIndex < tabs.count ? tabs.getTab(tabs.currentIndex).item : null
    property int previousVisibility: Window.Windowed
    width: 1300
    height: 900
    visible: true
    title: currentWebView && currentWebView.title
    Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint
    toolBar: ToolBar {...}
    // Necessary to accept server's certificate
    TabView {...}
    // Showing Manager Window
    WebEngineView {
        id: devToolsView
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        onNewViewRequested: function(request) {
            var tab = tabs.createEmptyTab(currentWebView.profile);
            tabs.currentIndex = tabs.count - 1;
            request.openIn(tab.item);
        }
    }
    // By-Passing the Server's Certificate using sslDialog
    MessageDialog {...}
    DownloadView {
        id: downloadView
        visible: false
        anchors.fill: parent
    }
    function onDownloadRequested(download) {
        downloadView.visible = true;
        downloadView.append(download);
        download.accept();
    }
}

Thank you very much for providing some guidance on how to solve this problem and how to move on.

Emanuele
  • 2,194
  • 6
  • 32
  • 71

1 Answers1

1

For a next occasion it is necessary to show what you have tried since saying that it does not work and using my previous code is not enough, each login depends on each system so there is no generic solution.

From the little HTML you have shown you can deduce that:

  • The username input has an id of "username".
  • The password input has an id of "password".
  • when the button is pressed, the function "LSubmit()" that makes the verification is called.

From my experiment with my router there is always a delay in the creation of the DOM, in addition to the url that entered a "/" is added at the end if it does not exist

Considering the above, the solution is:

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtWebEngine 1.9

ApplicationWindow{
    id: root
    width: 640
    height: 480
    visible: true

    property string username: "USERNAME"
    property string password: "PASSWORD"

    QtObject{
        id: internals
        property string login_script: "
            console.log('start');
            var input_username = document.getElementById('username');
            console.log('username');
            console.log(input_username);
            input_username.value = '%1';
            var input_password = document.getElementById('password');
            console.log('password');
            console.log(input_password);
            input_password.value = '%2';
            console.log('clicked');
            LSubmit();
            console.log('end');
        ".arg(username).arg(password);
    }

    Timer {
        id: timer
        interval: 1000; repeat: false
        onTriggered: view.runJavaScript(internals.login_script)
    }

    WebEngineView {
        id: view
        anchors.fill: parent
        onUrlChanged: {
            console.log(url)
            if(url == Qt.resolvedUrl("https://123.456.789.123:7878/")){
                timer.running = true   
            }
        }
        onCertificateError: function(error) {
            error.ignoreCertificateError();
        }
        Component.onCompleted: view.url = "https://123.456.789.123:7878"
    }
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks eyllanesc for taking the time to read the question. The problem is that, because I have to access to a page that requires a certificate, the "site can't be reached" is preventing from loading the page. As shown [here](https://i.imgur.com/x0SXC2m.png). The reason why I was using `Quick Nano Browser Example` is because there is the possibility of using the `ignoreCertificateError()` indicated [here](https://doc.qt.io/qt-5/qtwebengine-webengine-quicknanobrowser-example.html). – Emanuele Nov 18 '19 at 22:04
  • @Emanuele I recommend you not to use a code blindly, understand what is being done, experiment and play with the code. For example, the "Quick Nano Browser" code is interesting for this, and you should ask yourself: When calling MessageDialog that calls the ignoreCertificateError() slot? I get the impression that you want to use an extensive code without trying to understand it. mmm – eyllanesc Nov 18 '19 at 22:09
  • it almost works, I see the log-in page, but the line edit of the password is not auto-filling out with the password and I am trying to understand why. – Emanuele Nov 18 '19 at 22:23
  • @Emanuele Try running the js in the chrome console tab and check if it throws an error. Unfortunately I can no longer help you for obvious reasons. – eyllanesc Nov 18 '19 at 22:25
  • @Emanuele With my script you must print the url, what do you get? – eyllanesc Nov 18 '19 at 22:26
  • I get the correct [output](https://i.imgur.com/okzrcwv.png) as you can see. The password does not appear. But if I proceed manually with the password I enter normally. Would that be useful to share the whole `html` of the log-in? – Emanuele Nov 18 '19 at 22:32
  • So correctly the page ignores the certificate and proceed to the log in page. – Emanuele Nov 18 '19 at 22:33
  • @Emanuele I have added logs to analyze where the problem is, use the new script and show me what you get. – eyllanesc Nov 18 '19 at 22:36
  • I think it is useful, it seems that JS does not see some `html` values? See the out put of the [console](https://i.imgur.com/iU5WNPf.png). `js: Uncaught TypeError: Cannot set property 'value' of null` – Emanuele Nov 18 '19 at 22:42
  • @Emanuele I find it strange that the same url is printed twice, have you used my updated code? – eyllanesc Nov 18 '19 at 22:44
  • Sorry, now I did and in fact I get only one url, see [here](https://i.imgur.com/AmJ6bsP.png) – Emanuele Nov 18 '19 at 22:48
  • @Emanuele it seems that you are not using my code since I expected `start` to be printed – eyllanesc Nov 18 '19 at 22:50
  • could [this](https://www.dropbox.com/sh/fn0jle0wxppni4o/AACWfCF80AEVBgxV9-YJ9BpFa?dl=0) be helpful? – Emanuele Nov 18 '19 at 23:02
  • the only thing I am not showing is the password but other than that it is the last updated code. – Emanuele Nov 18 '19 at 23:06
  • So it wasn't the `html`. It was the time of execution of the request? – Emanuele Nov 18 '19 at 23:10
  • Thank you very much eyllanesc! that was very very helpful! I appreciate your time in helping me! – Emanuele Nov 18 '19 at 23:11
  • @Emanuele Exactly, the loading time was more than 1 second (which I had estimated) so the script was executed before the form was created by throwing the error. – eyllanesc Nov 18 '19 at 23:12