File indexing completed on 2024-04-28 05:02:33

0001 /*
0002     SPDX-FileCopyrightText: 2018 Ralf Habacker ralf.habacker @freenet.de
0003 
0004     This file is part of libalkimia.
0005 
0006     SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #include "alkwebpage.h"
0010 
0011 #if defined(BUILD_WITH_WEBENGINE)
0012 #include <klocalizedstring.h>
0013 
0014 #include <QContextMenuEvent>
0015 #include <QDesktopServices>
0016 #include <QEventLoop>
0017 #include <QMenu>
0018 #include <QUrl>
0019 #include <QWebEnginePage>
0020 #include <QWebEngineView>
0021 
0022 // Port used by web inspector, may be converted into a tool setting attribute
0023 static const int s_webInspectorPort{8181};
0024 static bool s_webInspectorEnabled{false};
0025 
0026 class AlkWebView: public QWebEngineView
0027 {
0028     Q_OBJECT
0029 public:
0030     AlkWebPage *q;
0031 
0032     explicit AlkWebView(AlkWebPage *parent)
0033         : QWebEngineView()
0034         , q(parent)
0035     {}
0036 
0037     void contextMenuEvent(QContextMenuEvent *event) override
0038     {
0039         if (!s_webInspectorEnabled) {
0040             QWebEngineView::contextMenuEvent(event);
0041             return;
0042         }
0043         QMenu *menu = page()->createStandardContextMenu();
0044         const QList<QAction *> actions = menu->actions();
0045         auto inspectElement = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement));
0046         if (inspectElement == actions.cend()) {
0047             auto viewSource = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::ViewSource));
0048             if (viewSource == actions.cend())
0049                 menu->addSeparator();
0050 
0051             QAction *action = new QAction(menu);
0052             action->setText(i18n("Open inspector in new window"));
0053             connect(action, &QAction::triggered, []() {
0054 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
0055                 QDesktopServices::openUrl(QString("http://localhost:%1/devtools/page/%2").arg(webInspectorPort, page()->devToolsId()));
0056 #else
0057                 QDesktopServices::openUrl(QString("http://localhost:%1").arg(s_webInspectorPort));
0058 #endif
0059             });
0060 
0061             QAction *before(inspectElement == actions.cend() ? nullptr : *inspectElement);
0062             menu->insertAction(before, action);
0063         } else {
0064             (*inspectElement)->setText(i18n("Inspect element"));
0065         }
0066         menu->popup(event->globalPos());
0067     }
0068 };
0069 
0070 class AlkWebPage::Private : public QObject
0071 {
0072     Q_OBJECT
0073 public:
0074     AlkWebPage *q;
0075     bool inspectorEnabled{false};
0076 
0077     explicit Private(AlkWebPage *_q)
0078         : q(_q)
0079     {
0080     }
0081     Private(const Private& right) = delete;
0082     Private& operator=(const Private& right) = delete;
0083 
0084     void slotUrlChanged(const QUrl &url)
0085     {
0086         // This workaround is necessary because QWebEnginePage::urlChanged()
0087         // returns the html content set with setContent() as url.
0088         if (url.scheme().startsWith("http"))
0089             Q_EMIT q->urlChanged(url);
0090         else
0091             Q_EMIT q->urlChanged(QUrl());
0092     }
0093 };
0094 
0095 AlkWebPage::AlkWebPage(QWidget *parent)
0096   : QWebEnginePage(parent)
0097   , d(new Private(this))
0098 {
0099     connect(this, &QWebEnginePage::urlChanged, d, &Private::slotUrlChanged);
0100 }
0101 
0102 AlkWebPage::~AlkWebPage()
0103 {
0104     delete d;
0105 }
0106 
0107 QWidget *AlkWebPage::widget()
0108 {
0109 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0110     if (!view())
0111         setView(new AlkWebView(this));
0112     return view();
0113 #else
0114     return QWebEngineView::forPage(this);
0115 #endif
0116 }
0117 
0118 void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage)
0119 {
0120     Q_UNUSED(acceptLanguage)
0121 
0122     setUrl(url);
0123 }
0124 
0125 QString AlkWebPage::toHtml()
0126 {
0127     QString html;
0128     QEventLoop loop;
0129     QWebEnginePage::toHtml([&html, &loop](const QString &result)
0130         {
0131             html = result;
0132             loop.quit();
0133         }
0134     );
0135     loop.exec();
0136     return html;
0137 }
0138 
0139 void AlkWebPage::setContent(const QString &s)
0140 {
0141     setHtml(s);
0142 }
0143 
0144 QString AlkWebPage::getFirstElement(const QString &symbol)
0145 {
0146     Q_UNUSED(symbol)
0147 
0148     return QString();
0149 }
0150 
0151 void AlkWebPage::setWebInspectorEnabled(bool state)
0152 {
0153     s_webInspectorEnabled = state;
0154     if (state)
0155         qputenv("QTWEBENGINE_REMOTE_DEBUGGING", QByteArray::number(s_webInspectorPort));
0156     else
0157         qunsetenv("QTWEBENGINE_REMOTE_DEBUGGING");
0158 }
0159 
0160 bool AlkWebPage::webInspectorEnabled()
0161 {
0162     return s_webInspectorEnabled;
0163 }
0164 
0165 #include "alkwebpage.moc"
0166 
0167 #elif defined(BUILD_WITH_WEBKIT)
0168 #include <QWebFrame>
0169 #include <QWebElement>
0170 #include <QWebInspector>
0171 #include <QWebView>
0172 #include <QNetworkRequest>
0173 
0174 class AlkWebPage::Private
0175 {
0176 public:
0177     QWebInspector *inspector;
0178     AlkWebPage *p;
0179     QNetworkAccessManager *networkAccessManager;
0180     explicit Private(AlkWebPage *parent)
0181       : inspector(nullptr),
0182         p(parent),
0183         networkAccessManager(new QNetworkAccessManager)
0184     {
0185 #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
0186         // see https://community.kde.org/Policies/API_to_Avoid#QNetworkAccessManager
0187         networkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
0188 #endif
0189         p->page()->setNetworkAccessManager(networkAccessManager);
0190     }
0191 
0192     ~Private()
0193     {
0194         p->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, false);
0195         inspector->setPage(nullptr);
0196         delete inspector;
0197         delete networkAccessManager;
0198     }
0199 
0200     void setWebInspectorEnabled(bool enable)
0201     {
0202         p->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enable);
0203         if (enable && !inspector) {
0204             inspector = new QWebInspector();
0205             inspector->setPage(p->page());
0206         }
0207     }
0208 
0209     bool webInspectorEnabled()
0210     {
0211         return p->page()->settings()->testAttribute(QWebSettings::DeveloperExtrasEnabled);
0212     }
0213 };
0214 
0215 AlkWebPage::AlkWebPage(QWidget *parent)
0216   : QWebView(parent)
0217   , d(new Private(this))
0218 {
0219     page()->settings()->setAttribute(QWebSettings::JavaEnabled, false);
0220     page()->settings()->setAttribute(QWebSettings::AutoLoadImages, false);
0221     page()->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
0222 }
0223 
0224 AlkWebPage::~AlkWebPage()
0225 {
0226     delete d;
0227 }
0228 
0229 QWidget *AlkWebPage::widget()
0230 {
0231     return this;
0232 }
0233 
0234 void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage)
0235 {
0236     QNetworkRequest request;
0237     request.setUrl(url);
0238     if (!acceptLanguage.isEmpty())
0239         request.setRawHeader("Accept-Language", acceptLanguage.toLocal8Bit());
0240 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
0241     if (url.query().toLower().contains(QLatin1String("method=post"))) {
0242         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
0243         QWebView::load(request, QNetworkAccessManager::PostOperation, url.query().toUtf8());
0244 #else
0245     if (url.hasQueryItem(QLatin1String("method")) && url.queryItemValue(QLatin1String("method")).toLower()== QLatin1String("post")) {
0246         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
0247         QWebView::load(request, QNetworkAccessManager::PostOperation);
0248 #endif
0249     } else
0250         QWebView::load(request);
0251 }
0252 
0253 QString AlkWebPage::toHtml()
0254 {
0255     QWebFrame *frame = page()->mainFrame();
0256     return frame->toHtml();
0257 }
0258 
0259 QString AlkWebPage::getFirstElement(const QString &symbol)
0260 {
0261     QWebFrame *frame = page()->mainFrame();
0262     QWebElement element = frame->findFirstElement(symbol);
0263     return element.toPlainText();
0264 }
0265 
0266 void AlkWebPage::setWebInspectorEnabled(bool enable)
0267 {
0268     d->setWebInspectorEnabled(enable);
0269 }
0270 
0271 bool AlkWebPage::webInspectorEnabled()
0272 {
0273     return d->webInspectorEnabled();
0274 }
0275 
0276 #else
0277 
0278 class AlkWebPage::Private
0279 {
0280 public:
0281 };
0282 
0283 AlkWebPage::AlkWebPage(QWidget *parent)
0284   : QTextBrowser(parent)
0285   , d(new Private)
0286 {
0287     setOpenExternalLinks(false);
0288     setOpenLinks(false);
0289 }
0290 
0291 AlkWebPage::~AlkWebPage()
0292 {
0293     delete d;
0294 }
0295 
0296 QWidget *AlkWebPage::widget()
0297 {
0298     return this;
0299 }
0300 
0301 void AlkWebPage::load(const QUrl &url, const QString &acceptLanguage)
0302 {
0303     Q_UNUSED(url)
0304     Q_UNUSED(acceptLanguage)
0305 }
0306 
0307 void AlkWebPage::setUrl(const QUrl &url)
0308 {
0309     Q_UNUSED(url)
0310 }
0311 
0312 void AlkWebPage::setContent(const QString &s)
0313 {
0314     setHtml(s);
0315 }
0316 
0317 QString AlkWebPage::getFirstElement(const QString &symbol)
0318 {
0319     Q_UNUSED(symbol)
0320 
0321     return QString();
0322 }
0323 
0324 void AlkWebPage::setWebInspectorEnabled(bool enable)
0325 {
0326     Q_UNUSED(enable)
0327 }
0328 
0329 bool AlkWebPage::webInspectorEnabled()
0330 {
0331     return false;
0332 }
0333 
0334 #endif