diff options
Diffstat (limited to 'examples/webenginewidgets/demobrowser/webview.cpp')
-rw-r--r-- | examples/webenginewidgets/demobrowser/webview.cpp | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/examples/webenginewidgets/demobrowser/webview.cpp b/examples/webenginewidgets/demobrowser/webview.cpp new file mode 100644 index 000000000..d56ab8025 --- /dev/null +++ b/examples/webenginewidgets/demobrowser/webview.cpp @@ -0,0 +1,488 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "browserapplication.h" +#include "browsermainwindow.h" +#include "cookiejar.h" +#include "downloadmanager.h" +#include "featurepermissionbar.h" +#include "ui_passworddialog.h" +#include "ui_proxy.h" +#include "tabwidget.h" +#include "webview.h" + +#include <QtGui/QClipboard> +#include <QtNetwork/QAuthenticator> +#include <QtNetwork/QNetworkReply> +#include <QtWidgets/QMenu> +#include <QtWidgets/QMessageBox> +#include <QtGui/QMouseEvent> + +#if defined(QWEBENGINEPAGE_HITTESTCONTENT) +#include <QWebEngineHitTestResult> +#endif + +#ifndef QT_NO_UITOOLS +#include <QtUiTools/QUiLoader> +#endif //QT_NO_UITOOLS + +#include <QtCore/QDebug> +#include <QtCore/QBuffer> + +WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) + : QWebEnginePage(profile, parent) + , m_keyboardModifiers(Qt::NoModifier) + , m_pressedButtons(Qt::NoButton) +{ +#if defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + setNetworkAccessManager(BrowserApplication::networkAccessManager()); +#endif +#if defined(QWEBENGINEPAGE_UNSUPPORTEDCONTENT) + connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), + this, SLOT(handleUnsupportedContent(QNetworkReply*))); +#endif + connect(this, SIGNAL(authenticationRequired(const QUrl &, QAuthenticator*)), + SLOT(authenticationRequired(const QUrl &, QAuthenticator*))); + connect(this, SIGNAL(proxyAuthenticationRequired(const QUrl &, QAuthenticator *, const QString &)), + SLOT(proxyAuthenticationRequired(const QUrl &, QAuthenticator *, const QString &))); +} + +BrowserMainWindow *WebPage::mainWindow() +{ + QObject *w = this->parent(); + while (w) { + if (BrowserMainWindow *mw = qobject_cast<BrowserMainWindow*>(w)) + return mw; + w = w->parent(); + } + return BrowserApplication::instance()->mainWindow(); +} + +bool WebPage::acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) +{ + Q_UNUSED(type); + if (isMainFrame) { + m_loadingUrl = url; + emit loadingUrl(m_loadingUrl); + } + return true; +} + +bool WebPage::certificateError(const QWebEngineCertificateError &error) +{ + if (error.isOverridable()) { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText(error.errorDescription()); + msgBox.setInformativeText(tr("If you wish so, you may continue with an unverified certificate. " + "Accepting an unverified certificate means " + "you may not be connected with the host you tried to connect to.\n" + "Do you wish to override the security check and continue?")); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::No); + return msgBox.exec() == QMessageBox::Yes; + } + QMessageBox::critical(view(), tr("Certificate Error"), error.errorDescription(), QMessageBox::Ok, QMessageBox::NoButton); + return false; +} + +class PopupWindow : public QWidget { + Q_OBJECT +public: + PopupWindow(QWebEngineProfile *profile) + : m_addressBar(new QLineEdit(this)) + , m_view(new WebView(this)) + { + m_view->setPage(new WebPage(profile, m_view)); + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + setLayout(layout); + layout->addWidget(m_addressBar); + layout->addWidget(m_view); + m_view->setFocus(); + + connect(m_view, &WebView::titleChanged, this, &QWidget::setWindowTitle); + connect(m_view, &WebView::urlChanged, this, &PopupWindow::setUrl); + connect(page(), &WebPage::geometryChangeRequested, this, &PopupWindow::adjustGeometry); + connect(page(), &WebPage::windowCloseRequested, this, &QWidget::close); + } + + QWebEnginePage* page() const { return m_view->page(); } + +private Q_SLOTS: + void setUrl(const QUrl &url) + { + m_addressBar->setText(url.toString()); + } + + void adjustGeometry(const QRect &newGeometry) + { + const int x1 = frameGeometry().left() - geometry().left(); + const int y1 = frameGeometry().top() - geometry().top(); + const int x2 = frameGeometry().right() - geometry().right(); + const int y2 = frameGeometry().bottom() - geometry().bottom(); + + setGeometry(newGeometry.adjusted(x1, y1 - m_addressBar->height(), x2, y2)); + } + +private: + QLineEdit *m_addressBar; + WebView *m_view; + +}; + +#include "webview.moc" + +QWebEnginePage *WebPage::createWindow(QWebEnginePage::WebWindowType type) +{ + if (type == QWebEnginePage::WebBrowserTab) { + return mainWindow()->tabWidget()->newTab()->page(); + } else if (type == QWebEnginePage::WebBrowserWindow) { + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + return mainWindow->currentTab()->page(); + } else { + PopupWindow *popup = new PopupWindow(profile()); + popup->setAttribute(Qt::WA_DeleteOnClose); + popup->show(); + return popup->page(); + } +} + +#if !defined(QT_NO_UITOOLS) +QObject *WebPage::createPlugin(const QString &classId, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues) +{ + Q_UNUSED(url); + Q_UNUSED(paramNames); + Q_UNUSED(paramValues); + QUiLoader loader; + return loader.createWidget(classId, view()); +} +#endif // !defined(QT_NO_UITOOLS) + +#if defined(QWEBENGINEPAGE_UNSUPPORTEDCONTENT) +void WebPage::handleUnsupportedContent(QNetworkReply *reply) +{ + QString errorString = reply->errorString(); + + if (m_loadingUrl != reply->url()) { + // sub resource of this page + qWarning() << "Resource" << reply->url().toEncoded() << "has unknown Content-Type, will be ignored."; + reply->deleteLater(); + return; + } + + if (reply->error() == QNetworkReply::NoError && !reply->header(QNetworkRequest::ContentTypeHeader).isValid()) { + errorString = "Unknown Content-Type"; + } + + QFile file(QLatin1String(":/notfound.html")); + bool isOpened = file.open(QIODevice::ReadOnly); + Q_ASSERT(isOpened); + Q_UNUSED(isOpened) + + QString title = tr("Error loading page: %1").arg(reply->url().toString()); + QString html = QString(QLatin1String(file.readAll())) + .arg(title) + .arg(errorString) + .arg(reply->url().toString()); + + QBuffer imageBuffer; + imageBuffer.open(QBuffer::ReadWrite); + QIcon icon = view()->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, view()); + QPixmap pixmap = icon.pixmap(QSize(32,32)); + if (pixmap.save(&imageBuffer, "PNG")) { + html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"), + QString(QLatin1String(imageBuffer.buffer().toBase64()))); + } + + QList<QWebEngineFrame*> frames; + frames.append(mainFrame()); + while (!frames.isEmpty()) { + QWebEngineFrame *frame = frames.takeFirst(); + if (frame->url() == reply->url()) { + frame->setHtml(html, reply->url()); + return; + } + QList<QWebEngineFrame *> children = frame->childFrames(); + foreach (QWebEngineFrame *frame, children) + frames.append(frame); + } + if (m_loadingUrl == reply->url()) { + mainFrame()->setHtml(html, reply->url()); + } +} +#endif + +void WebPage::authenticationRequired(const QUrl &requestUrl, QAuthenticator *auth) +{ + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui::PasswordDialog passwordDialog; + passwordDialog.setupUi(&dialog); + + passwordDialog.iconLabel->setText(QString()); + passwordDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = tr("<qt>Enter username and password for \"%1\" at %2</qt>"); + introMessage = introMessage.arg(auth->realm()).arg(requestUrl.toString().toHtmlEscaped()); + passwordDialog.introLabel->setText(introMessage); + passwordDialog.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(passwordDialog.userNameLineEdit->text()); + auth->setPassword(passwordDialog.passwordLineEdit->text()); + } else { + // Set authenticator null if dialog is cancelled + *auth = QAuthenticator(); + } +} + +void WebPage::proxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth, const QString &proxyHost) +{ + Q_UNUSED(requestUrl); + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + + QDialog dialog(mainWindow); + dialog.setWindowFlags(Qt::Sheet); + + Ui::ProxyDialog proxyDialog; + proxyDialog.setupUi(&dialog); + + proxyDialog.iconLabel->setText(QString()); + proxyDialog.iconLabel->setPixmap(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, mainWindow).pixmap(32, 32)); + + QString introMessage = tr("<qt>Connect to proxy \"%1\" using:</qt>"); + introMessage = introMessage.arg(proxyHost.toHtmlEscaped()); + proxyDialog.introLabel->setText(introMessage); + proxyDialog.introLabel->setWordWrap(true); + + if (dialog.exec() == QDialog::Accepted) { + auth->setUser(proxyDialog.userNameLineEdit->text()); + auth->setPassword(proxyDialog.passwordLineEdit->text()); + } else { + // Set authenticator null if dialog is cancelled + *auth = QAuthenticator(); + } +} + +void WebPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int /*lineNumber*/, const QString& sourceID) +{ + QUrl url; + url.setUrl(sourceID); + switch (level) { + case InfoMessageLevel: + // Ignore these, they can still be found in the inspector. + break; + case WarningMessageLevel: + qInfo() << "JavaScript WARNING:" << url.host() << message; + break; + case ErrorMessageLevel: + qInfo() << "JavaScript ERROR:" << url.host() << message; + break; + } +} + +WebView::WebView(QWidget* parent) + : QWebEngineView(parent) + , m_progress(0) + , m_page(0) + , m_iconReply(0) +{ + connect(this, SIGNAL(loadProgress(int)), + this, SLOT(setProgress(int))); + connect(this, SIGNAL(loadFinished(bool)), + this, SLOT(loadFinished(bool))); +} + +void WebView::setPage(WebPage *_page) +{ + if (m_page) + m_page->deleteLater(); + m_page = _page; + QWebEngineView::setPage(_page); +#if defined(QWEBENGINEPAGE_STATUSBARMESSAGE) + connect(page(), SIGNAL(statusBarMessage(QString)), + SLOT(setStatusBarText(QString))); +#endif + connect(page(), SIGNAL(loadingUrl(QUrl)), + this, SIGNAL(urlChanged(QUrl))); + connect(page(), SIGNAL(iconUrlChanged(QUrl)), + this, SLOT(onIconUrlChanged(QUrl))); + connect(page(), &WebPage::featurePermissionRequested, this, &WebView::onFeaturePermissionRequested); +#if defined(QWEBENGINEPAGE_UNSUPPORTEDCONTENT) + page()->setForwardUnsupportedContent(true); +#endif +} + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = page()->createStandardContextMenu(); + const QList<QAction*> actions = menu->actions(); + QList<QAction*>::const_iterator it = qFind(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::OpenLinkInThisWindow)); + if (it != actions.cend()) { + (*it)->setText(tr("Open Link in This Window")); + ++it; + menu->insertAction(*it, page()->action(QWebEnginePage::OpenLinkInNewWindow)); + menu->insertAction(*it, page()->action(QWebEnginePage::OpenLinkInNewTab)); + } + + menu->popup(event->globalPos()); +} + +void WebView::wheelEvent(QWheelEvent *event) +{ +#if defined(QWEBENGINEPAGE_SETTEXTSIZEMULTIPLIER) + if (QApplication::keyboardModifiers() & Qt::ControlModifier) { + int numDegrees = event->delta() / 8; + int numSteps = numDegrees / 15; + setTextSizeMultiplier(textSizeMultiplier() + numSteps * 0.1); + event->accept(); + return; + } +#endif + QWebEngineView::wheelEvent(event); +} + +void WebView::openLinkInNewTab() +{ + pageAction(QWebEnginePage::OpenLinkInNewTab)->trigger(); +} + +void WebView::onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature) +{ + FeaturePermissionBar *permissionBar = new FeaturePermissionBar(this); + connect(permissionBar, &FeaturePermissionBar::featurePermissionProvided, page(), &QWebEnginePage::setFeaturePermission); + + // Discard the bar on new loads (if we navigate away or reload). + connect(page(), &QWebEnginePage::loadStarted, permissionBar, &QObject::deleteLater); + + permissionBar->requestPermission(securityOrigin, feature); +} + +void WebView::setProgress(int progress) +{ + m_progress = progress; +} + +void WebView::loadFinished(bool success) +{ + if (success && 100 != m_progress) { + qWarning() << "Received finished signal while progress is still:" << progress() + << "Url:" << url(); + } + m_progress = 0; +} + +void WebView::loadUrl(const QUrl &url) +{ + m_initialUrl = url; + load(url); +} + +QString WebView::lastStatusBarText() const +{ + return m_statusBarText; +} + +QUrl WebView::url() const +{ + QUrl url = QWebEngineView::url(); + if (!url.isEmpty()) + return url; + + return m_initialUrl; +} + +QIcon WebView::icon() const +{ + if (!m_icon.isNull()) + return m_icon; + return BrowserApplication::instance()->defaultIcon(); +} + +void WebView::onIconUrlChanged(const QUrl &url) +{ + QNetworkRequest iconRequest(url); + m_iconReply = BrowserApplication::networkAccessManager()->get(iconRequest); + m_iconReply->setParent(this); + connect(m_iconReply, SIGNAL(finished()), this, SLOT(iconLoaded())); +} + +void WebView::iconLoaded() +{ + m_icon = QIcon(); + if (m_iconReply) { + QByteArray data = m_iconReply->readAll(); + QPixmap pixmap; + pixmap.loadFromData(data); + m_icon.addPixmap(pixmap); + m_iconReply->deleteLater(); + m_iconReply = 0; + } + emit iconChanged(); +} + +void WebView::mousePressEvent(QMouseEvent *event) +{ + m_page->m_pressedButtons = event->buttons(); + m_page->m_keyboardModifiers = event->modifiers(); + QWebEngineView::mousePressEvent(event); +} + +void WebView::mouseReleaseEvent(QMouseEvent *event) +{ + QWebEngineView::mouseReleaseEvent(event); + if (!event->isAccepted() && (m_page->m_pressedButtons & Qt::MidButton)) { + QUrl url(QApplication::clipboard()->text(QClipboard::Selection)); + if (!url.isEmpty() && url.isValid() && !url.scheme().isEmpty()) { + setUrl(url); + } + } +} + +void WebView::setStatusBarText(const QString &string) +{ + m_statusBarText = string; +} |