File indexing completed on 2024-12-01 04:37:42
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