3#include <QContextMenuEvent> 
    6#include <QAuthenticator> 
    9#include <QLoggingCategory> 
   10#include <QWebEngineProfile> 
   11#include <QWebEngineScript> 
   12#include <QWebEngineScriptCollection> 
   14#include "FrmWebView.h" 
   15#include "FrmWebBrowser.h" 
   16#include "DlgUserPassword.h" 
   18static Q_LOGGING_CATEGORY(log, 
"WebBrowser.View")
 
   21    : QWebEngineView(parent)
 
   22    , m_pBrowser(pFrmWebBrowser)
 
   23#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) 
   24    , m_pDlgWebAuth(
nullptr)
 
   26    , m_pWebChannel(
nullptr)
 
   27    , m_pPasswordStore(
nullptr)
 
   30    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
   31    setMinimumSize(200, 100);
 
   33    check = connect(
this, &QWebEngineView::loadStarted, [
this]() {
 
   35        emit favIconChanged(favIcon());
 
   38    check = connect(
this, &QWebEngineView::loadProgress, [
this](
int progress) {
 
   39        m_loadProgress = progress;
 
   42    check = connect(
this, &QWebEngineView::loadFinished, [
this](
bool success) {
 
   43        m_loadProgress = success ? 100 : -1;
 
   44        emit favIconChanged(favIcon());
 
   45        if(m_pBrowser->m_pPara->GetAutoFillUserAndPassword() && m_pWebChannel && m_pPasswordStore)
 
   46            InjectScriptAutoFill();
 
   48        QString szUser, szPassword;
 
   49        int nRet = GetUserAndPassword(this->url(), szUser, szPassword);
 
   51            SetupAutoFillScript();
 
   52            GlobalFillForm(szUser, szPassword);
 
   57    check = connect(
this, &QWebEngineView::iconChanged, [
this](
const QIcon &) {
 
   58        emit favIconChanged(favIcon());
 
   61    check = connect(
this, &QWebEngineView::renderProcessTerminated,
 
   62            [
this](QWebEnginePage::RenderProcessTerminationStatus termStatus, 
int statusCode) {
 
   65        case QWebEnginePage::NormalTerminationStatus:
 
   66            status = tr(
"Render process normal exit");
 
   68        case QWebEnginePage::AbnormalTerminationStatus:
 
   69            status = tr(
"Render process abnormal exit");
 
   71        case QWebEnginePage::CrashedTerminationStatus:
 
   72            status = tr(
"Render process crashed");
 
   74        case QWebEnginePage::KilledTerminationStatus:
 
   75            status = tr(
"Render process killed");
 
   78        QMessageBox::StandardButton btn = QMessageBox::question(window(), status,
 
   79                                                   tr(
"Render process exited with code: %1\n" 
   80                                                      "Do you want to reload the page ?").arg(statusCode));
 
   81        if (btn == QMessageBox::Yes)
 
   82            QTimer::singleShot(0, 
this, &CFrmWebView::reload);
 
   87CFrmWebView::~CFrmWebView()
 
   89    qDebug(log) << Q_FUNC_INFO;
 
   90    if (m_imageAnimationGroup)
 
   91        delete m_imageAnimationGroup;
 
   93    m_imageAnimationGroup = 
nullptr;
 
   96#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
   97inline QString questionForPermissionType(QWebEnginePermission::PermissionType permissionType)
 
   99    switch (permissionType) {
 
  100    case QWebEnginePermission::PermissionType::Geolocation:
 
  101        return QObject::tr(
"Allow %1 to access your location information?");
 
  102    case QWebEnginePermission::PermissionType::MediaAudioCapture:
 
  103        return QObject::tr(
"Allow %1 to access your microphone?");
 
  104    case QWebEnginePermission::PermissionType::MediaVideoCapture:
 
  105        return QObject::tr(
"Allow %1 to access your webcam?");
 
  106    case QWebEnginePermission::PermissionType::MediaAudioVideoCapture:
 
  107        return QObject::tr(
"Allow %1 to access your microphone and webcam?");
 
  108    case QWebEnginePermission::PermissionType::MouseLock:
 
  109        return QObject::tr(
"Allow %1 to lock your mouse cursor?");
 
  110    case QWebEnginePermission::PermissionType::DesktopVideoCapture:
 
  111        return QObject::tr(
"Allow %1 to capture video of your desktop?");
 
  112    case QWebEnginePermission::PermissionType::DesktopAudioVideoCapture:
 
  113        return QObject::tr(
"Allow %1 to capture audio and video of your desktop?");
 
  114    case QWebEnginePermission::PermissionType::Notifications:
 
  115        return QObject::tr(
"Allow %1 to show notification on your desktop?");
 
  116    case QWebEnginePermission::PermissionType::ClipboardReadWrite:
 
  117        return QObject::tr(
"Allow %1 to read from and write to the clipboard?");
 
  118    case QWebEnginePermission::PermissionType::LocalFontsAccess:
 
  119        return QObject::tr(
"Allow %1 to access fonts stored on this machine?");
 
  120    case QWebEnginePermission::PermissionType::Unsupported:
 
  127void CFrmWebView::setPage(QWebEnginePage *page)
 
  130    CreateWebActionTrigger(page,QWebEnginePage::Forward);
 
  131    CreateWebActionTrigger(page,QWebEnginePage::Back);
 
  132    CreateWebActionTrigger(page,QWebEnginePage::Reload);
 
  133    CreateWebActionTrigger(page,QWebEnginePage::Stop);
 
  135    if (
auto oldPage = QWebEngineView::page()) {
 
  136        oldPage->disconnect(
this);
 
  138    QWebEngineView::setPage(page);
 
  139    connect(page, &QWebEnginePage::linkHovered, 
this, &CFrmWebView::sigLinkHovered);
 
  140    connect(page, &QWebEnginePage::windowCloseRequested,
 
  141            this, &CFrmWebView::sigCloseRequested);
 
  142    connect(page, &QWebEnginePage::selectClientCertificate,
 
  143            this, &CFrmWebView::slotSelectClientCertificate);
 
  144    connect(page, &QWebEnginePage::authenticationRequired, 
this,
 
  145            &CFrmWebView::slotAuthenticationRequired);
 
  146    connect(page, &QWebEnginePage::proxyAuthenticationRequired, 
this,
 
  147            &CFrmWebView::slotProxyAuthenticationRequired);   
 
  148    connect(page, &QWebEnginePage::registerProtocolHandlerRequested, 
this,
 
  150    #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
  151    connect(page, &QWebEnginePage::certificateError,
 
  152            this, &CFrmWebView::slotCertificateError);
 
  153    connect(page, &QWebEnginePage::permissionRequested, 
this,
 
  154            &CFrmWebView::slotPermissionRequested);
 
  156    #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) 
  157    connect(page, &QWebEnginePage::fileSystemAccessRequested, 
this,
 
  160    #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) 
  161    connect(page, &QWebEnginePage::desktopMediaRequested, 
this,
 
  162            &CFrmWebView::slotDesktopMediaRequest);
 
  163    connect(page, &QWebEnginePage::webAuthUxRequested,
 
  164            this, &CFrmWebView::slotWebAuthUxRequested);
 
  167    Q_ASSERT(m_pBrowser);
 
  168    if(m_pBrowser->m_pPara->GetAutoFillUserAndPassword()) {
 
  170            m_pWebChannel = 
new QWebChannel(
this);
 
  171        if(!m_pPasswordStore)
 
  173        if(m_pWebChannel && m_pPasswordStore) {
 
  175            m_pWebChannel->registerObject(QStringLiteral(
"PasswordManager"), m_pPasswordStore);
 
  176            page->setWebChannel(m_pWebChannel);
 
  177            InjectScriptQWebChannel();
 
  179            qCritical(log) << 
"Don't register PasswordManager";
 
  183int CFrmWebView::progress()
 const 
  185    return m_loadProgress;
 
  188QIcon CFrmWebView::favIcon()
 const 
  190    QIcon favIcon = icon();
 
  191    if (!favIcon.isNull())
 
  194    if (m_loadProgress < 0) {
 
  195        static QIcon errorIcon(
"dialog-error");
 
  198    if (m_loadProgress < 100) {
 
  199        static QIcon loadingIcon = QIcon::fromTheme(
"view-refresh");
 
  203    static QIcon defaultIcon(
"text-html");
 
  207QWebEngineView *CFrmWebView::createWindow(QWebEnginePage::WebWindowType type)
 
  210        return m_pBrowser->CreateWindow(
 
  211            type, this->page()->profile()->isOffTheRecord());
 
  215void CFrmWebView::CreateWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction webAction)
 
  217    QAction *action = page->action(webAction);
 
  218#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
  219    connect(action, &QAction::enabledChanged, [
this, action, webAction](
bool enabled){
 
  220        qDebug(log) << 
"webAction:" << webAction << enabled;
 
  221        emit sigWebActionEnabledChanged(webAction, enabled);
 
  224    connect(action, &QAction::changed, [
this, action, webAction]{
 
  225        emit sigWebActionEnabledChanged(webAction, action->isEnabled());
 
  230void CFrmWebView::contextMenuEvent(QContextMenuEvent *event)
 
  233#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 
  234    menu = createStandardContextMenu();
 
  236    menu = page()->createStandardContextMenu();
 
  238    const QList<QAction *> actions = menu->actions();
 
  239    auto inspectElement = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement));
 
  240    if (inspectElement == actions.cend()) {
 
  241        auto viewSource = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::ViewSource));
 
  242        if (viewSource == actions.cend())
 
  243            menu->addSeparator();
 
  244        QAction *action = menu->addAction(tr(
"Open inspector"));
 
  245        connect(action, &QAction::triggered,
 
  246                [
this]() { emit sigDevToolsRequested(page());});
 
  248        (*inspectElement)->setText(tr(
"Inspect element"));
 
  250#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
  252    QMenu *editImageAnimation = 
new QMenu(tr(
"Image animation policy"));
 
  254    m_imageAnimationGroup = 
new QActionGroup(editImageAnimation);
 
  255    m_imageAnimationGroup->setExclusive(
true);
 
  257    QAction *disableImageAnimation =
 
  258            editImageAnimation->addAction(tr(
"Disable all image animation"));
 
  259    disableImageAnimation->setCheckable(
true);
 
  260    m_imageAnimationGroup->addAction(disableImageAnimation);
 
  261    connect(disableImageAnimation, &QAction::triggered, [
this]() {
 
  262        handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Disallow);
 
  264    QAction *allowImageAnimationOnce =
 
  265            editImageAnimation->addAction(tr(
"Allow animated images, but only once"));
 
  266    allowImageAnimationOnce->setCheckable(
true);
 
  267    m_imageAnimationGroup->addAction(allowImageAnimationOnce);
 
  268    connect(allowImageAnimationOnce, &QAction::triggered,
 
  269            [
this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::AnimateOnce); });
 
  270    QAction *allowImageAnimation = editImageAnimation->addAction(tr(
"Allow all animated images"));
 
  271    allowImageAnimation->setCheckable(
true);
 
  272    m_imageAnimationGroup->addAction(allowImageAnimation);
 
  273    connect(allowImageAnimation, &QAction::triggered, [
this]() {
 
  274        handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Allow);
 
  277    switch (page()->settings()->imageAnimationPolicy()) {
 
  278    case QWebEngineSettings::ImageAnimationPolicy::Allow:
 
  279        allowImageAnimation->setChecked(
true);
 
  281    case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce:
 
  282        allowImageAnimationOnce->setChecked(
true);
 
  284    case QWebEngineSettings::ImageAnimationPolicy::Disallow:
 
  285        disableImageAnimation->setChecked(
true);
 
  288        allowImageAnimation->setChecked(
true);
 
  292    menu->addMenu(editImageAnimation);
 
  294    menu->popup(event->globalPos());
 
  297void CFrmWebView::slotSelectClientCertificate(QWebEngineClientCertificateSelection clientCertSelection)
 
  299    qDebug(log) << Q_FUNC_INFO;
 
  300    if(clientCertSelection.certificates().size() > 0) {
 
  302        clientCertSelection.select(clientCertSelection.certificates().at(0));
 
  306#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
  307void CFrmWebView::slotCertificateError(QWebEngineCertificateError error)
 
  309    qDebug(log) << Q_FUNC_INFO;
 
  313    if (!error.isMainFrame()) {
 
  314        error.rejectCertificate();
 
  320    szMsg = error.description() + 
"\n\n";
 
  321    szMsg += tr(
"If you wish so, you may continue with an unverified certificate. " 
  322                "Accepting an unverified certificate mean you may not be connected with the host you tried to connect to.") +
 
  323                "\n\n" + tr(
"Do you wish to override the security check and continue ?");
 
  324    int nRet = QMessageBox::critical(window(), tr(
"Certificate Error"), szMsg,
 
  325                          QMessageBox::StandardButton::Yes
 
  326                              | QMessageBox::StandardButton::No,
 
  327                          QMessageBox::StandardButton::No);
 
  328    if(QMessageBox::StandardButton::Yes == nRet)
 
  329        error.acceptCertificate();
 
  331        error.rejectCertificate();
 
  340void CFrmWebView::slotAuthenticationRequired(
const QUrl &requestUrl, QAuthenticator *auth)
 
  342    qDebug(log) << Q_FUNC_INFO;
 
  345    dlg.SetUser(tr(
"Set user and password") + 
"\n" + requestUrl.toString(), &user);
 
  346    if (dlg.exec() == QDialog::Accepted) {
 
  347        auth->setUser(user.GetName());
 
  348        auth->setPassword(user.GetPassword());
 
  351        *auth = QAuthenticator();
 
  355void CFrmWebView::slotProxyAuthenticationRequired(
const QUrl &url, QAuthenticator *auth,
 
  356                                                  const QString &proxyHost)
 
  358    qDebug(log) << Q_FUNC_INFO;
 
  361    dlg.SetUser(tr(
"Set user and password of proxy") + 
"\n" + proxyHost.toHtmlEscaped(), &user);
 
  362    if (dlg.exec() == QDialog::Accepted) {
 
  363        auth->setUser(user.GetName());
 
  364        auth->setPassword(user.GetPassword());
 
  367        *auth = QAuthenticator();
 
  396#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) 
  397void CFrmWebView::slotPermissionRequested(QWebEnginePermission permission)
 
  399    qDebug(log) << Q_FUNC_INFO;
 
  400    QString title = tr(
"Permission Request");
 
  401    QString question = questionForPermissionType(permission.permissionType()).arg(permission.origin().host());
 
  402    if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes)
 
  408void CFrmWebView::handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy policy)
 
  410    qDebug(log) << Q_FUNC_INFO;
 
  414    page()->settings()->setImageAnimationPolicy(policy);
 
  418#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) 
  419void CFrmWebView::slotDesktopMediaRequest(
const QWebEngineDesktopMediaRequest &request)
 
  421    qDebug(log) << Q_FUNC_INFO;
 
  423    request.selectScreen(request.screensModel()->index(0));
 
  426void CFrmWebView::slotWebAuthUxRequested(QWebEngineWebAuthUxRequest *request)
 
  428    qDebug(log) << Q_FUNC_INFO;
 
  431        delete m_pDlgWebAuth;
 
  433    m_pDlgWebAuth = 
new CDlgWebAuth(request, window());
 
  434    m_pDlgWebAuth->setModal(
false);
 
  435    m_pDlgWebAuth->setWindowFlags(m_pDlgWebAuth->windowFlags() & ~Qt::WindowContextHelpButtonHint);
 
  437    connect(request, &QWebEngineWebAuthUxRequest::stateChanged, 
this, &CFrmWebView::onStateChanged);
 
  438    m_pDlgWebAuth->show();
 
  442void CFrmWebView::onStateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState state)
 
  444    qDebug(log) << Q_FUNC_INFO;
 
  446    if (QWebEngineWebAuthUxRequest::WebAuthUxState::Completed == state
 
  447        || QWebEngineWebAuthUxRequest::WebAuthUxState::Cancelled == state) {
 
  449            delete m_pDlgWebAuth;
 
  450            m_pDlgWebAuth = 
nullptr;
 
  453        m_pDlgWebAuth->updateDisplay();
 
  460        QWebEngineRegisterProtocolHandlerRequest request)
 
  462    qDebug(log) << Q_FUNC_INFO;
 
  463    auto answer = QMessageBox::question(window(), tr(
"Permission Request"),
 
  464                                        tr(
"Allow %1 to open all %2 links?")
 
  465                                                .arg(request.origin().host())
 
  466                                                .arg(request.scheme()));
 
  467    if (answer == QMessageBox::Yes)
 
 
  474#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) 
  477    qDebug(log) << Q_FUNC_INFO;
 
  479    switch (request.accessFlags()) {
 
  480    case QWebEngineFileSystemAccessRequest::Read:
 
  483    case QWebEngineFileSystemAccessRequest::Write:
 
  484        accessType = 
"write";
 
  486    case QWebEngineFileSystemAccessRequest::Read | QWebEngineFileSystemAccessRequest::Write:
 
  487        accessType = 
"read and write";
 
  493    auto answer = QMessageBox::question(window(), tr(
"File system access request"),
 
  494                                        tr(
"Give %1 %2 access to %3?")
 
  495                                                .arg(request.origin().host())
 
  497                                                .arg(request.filePath().toString()));
 
  498    if (answer == QMessageBox::Yes)
 
 
  505QString CFrmWebView::AutoFillForm()
 
  509                function autoFillForm(username, password) { 
  511                    var usernameFields = [ 
  512                        'input[type="text"]', 
  513                        'input[type="email"]', 
  514                        'input[name="username"]', 
  515                        'input[name="user"]', 
  516                        'input[name="email"]', 
  517                        'input[id="username"]', 
  523                    var passwordFields = [ 
  524                        'input[type="password"]', 
  525                        'input[name="password"]', 
  526                        'input[name="pass"]', 
  527                        'input[id="password"]', 
  532                    var arrPassword = []; 
  535                    usernameFields.forEach(function(selector) { 
  536                        var field = document.querySelector(selector); 
  537                        if (field && !field.value) { 
  540                            //var event = new Event('input', { bubbles: true }); 
  541                            //field.dispatchEvent(event); 
  546                    passwordFields.forEach(function(selector) { 
  547                        var field = document.querySelector(selector); 
  548                        if (field && !field.value) { 
  549                            arrPassword.push(field); 
  551                            //var event = new Event('input', { bubbles: true }); 
  552                            //field.dispatchEvent(event); 
  556                    arrUser.forEach(function(input) { 
  557                        if(!input.disabled && !input.readOnly) 
  558                            input.value = username; 
  560                    arrPassword.forEach(function(input) { 
  561                        if(!input.disabled && !input.readOnly) 
  562                            input.value = password; 
  568void CFrmWebView::SetupAutoFillScript() {
 
  570    QString autoFillScript = 
"(function() {";
 
  571    autoFillScript += AutoFillForm();
 
  572    autoFillScript += R
"( 
  574                window.autoFillForm = autoFillForm; 
  578    QWebEngineScript script; 
  579    script.setSourceCode(autoFillScript); 
  580    script.setInjectionPoint(QWebEngineScript::DocumentCreation); 
  581    script.setWorldId(QWebEngineScript::MainWorld); 
  582    page()->scripts().insert(script); 
  585void CFrmWebView::GlobalFillForm(
const QString &szUse, 
const QString &szPassword)
 
  587    QString js = QString(
"window.autoFillForm('%1', '%2');").arg(szUse, szPassword);
 
  588    page()->runJavaScript(js);
 
  591void CFrmWebView::FillForm(
const QString &szUse, 
const QString &szPassword)
 
  593    QString js = AutoFillForm();
 
  594    js += QString(
"autoFillForm('%1', '%2');").arg(szUse, szPassword);
 
  595    page()->runJavaScript(js);
 
  598int CFrmWebView::GetUserAndPassword(QUrl url, QString &szUser, QString &szPassword)
 
  607void CFrmWebView::InjectScriptQWebChannel()
 
  613    auto loadResource = [](
const QString &rcPath)->QString{
 
  615        if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
 
  616            qCritical(log) << 
"Cannot open resource" << rcPath;
 
  619        return QString::fromUtf8(f.readAll());
 
  622    QString qwebchannelJs = loadResource(
":/js/QWebChannel.js");
 
  623    if (qwebchannelJs.isEmpty()) {
 
  624        qCritical(log) << 
"QWebChannel.js not found in resources!";
 
  629    QWebEngineScript channelScript;
 
  630    channelScript.setName(
"qwebchannel.js");
 
  631    channelScript.setInjectionPoint(QWebEngineScript::DocumentCreation);
 
  632    channelScript.setRunsOnSubFrames(
true);
 
  633    channelScript.setWorldId(QWebEngineScript::MainWorld);
 
  634    channelScript.setSourceCode(qwebchannelJs);
 
  635    page()->scripts().insert(channelScript);
 
  638void CFrmWebView::InjectScriptAutoFill()
 
  644    auto loadResource = [](
const QString &rcPath)->QString{
 
  646        if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
 
  647            qCritical(log) << 
"Cannot open resource" << rcPath;
 
  650        return QString::fromUtf8(f.readAll());
 
  653    QString autofillJs = loadResource(
":/js/AutoFill.js");
 
  654    if (autofillJs.isEmpty()) {
 
  655        qCritical(log) << 
"AutoFill.js not found in resources!";
 
  666    page()->runJavaScript(autofillJs);
 
void slotFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request)
[registerProtocolHandlerRequested]
 
void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request)
[registerProtocolHandlerRequested]
 
It contains user and password It only valid in plugin.