diff options
Diffstat (limited to 'examples/webenginewidgets/browser/webview.cpp')
-rw-r--r-- | examples/webenginewidgets/browser/webview.cpp | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/examples/webenginewidgets/browser/webview.cpp b/examples/webenginewidgets/browser/webview.cpp new file mode 100644 index 000000000..193cb4797 --- /dev/null +++ b/examples/webenginewidgets/browser/webview.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt 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 "networkaccessmanager.h" +#include "ui_passworddialog.h" +#include "ui_proxy.h" +#include "tabwidget.h" +#include "webview.h" + +#include <QtGui/QClipboard> +#include <QtNetwork/QAuthenticator> +#include <QtWidgets/QMenu> +#include <QtWidgets/QMessageBox> +#include <QtGui/QMouseEvent> + +#include <QWebEngineHitTestResult> + +#ifndef QT_NO_UITOOLS +#include <QtUiTools/QUiLoader> +#endif //QT_NO_UITOOLS + +#include <QtCore/QDebug> +#include <QtCore/QBuffer> + +WebPage::WebPage(QObject *parent) + : QWebEnginePage(parent) + , m_keyboardModifiers(Qt::NoModifier) + , m_pressedButtons(Qt::NoButton) + , m_openInNewTab(false) +{ +#if defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + setNetworkAccessManager(BrowserApplication::networkAccessManager()); +#endif + connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), + this, SLOT(handleUnsupportedContent(QNetworkReply*))); + 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(QWebEngineFrame *frame, const QNetworkRequest &request, NavigationType type) +{ +#if defined(QWEBENGINEPAGE_ACCEPTNAVIGATIONREQUEST) + // ctrl open in new tab + // ctrl-shift open in new tab and select + // ctrl-alt open in new window + if (type == QWebEnginePage::NavigationTypeLinkClicked + && (m_keyboardModifiers & Qt::ControlModifier + || m_pressedButtons == Qt::MidButton)) { + bool newWindow = (m_keyboardModifiers & Qt::AltModifier); + WebView *webView; + if (newWindow) { + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *newMainWindow = BrowserApplication::instance()->mainWindow(); + webView = newMainWindow->currentTab(); + newMainWindow->raise(); + newMainWindow->activateWindow(); + webView->setFocus(); + } else { + bool selectNewTab = (m_keyboardModifiers & Qt::ShiftModifier); + webView = mainWindow()->tabWidget()->newTab(selectNewTab); + } + webView->load(request); + m_keyboardModifiers = Qt::NoModifier; + m_pressedButtons = Qt::NoButton; + return false; + } + m_loadingUrl = request.url(); + emit loadingUrl(m_loadingUrl); +#endif + return QWebEnginePage::acceptNavigationRequest(frame, request, type); +} + +class PopupWindow : public QWidget { + Q_OBJECT +public: + PopupWindow() + : m_addressBar(new QLineEdit(this)) + , m_view(new WebView(this)) + { + 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 (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton) + m_openInNewTab = true; + if (m_openInNewTab) { + m_openInNewTab = false; + return mainWindow()->tabWidget()->newTab()->page(); + } + if (type == QWebEnginePage::WebBrowserWindow) { + BrowserApplication::instance()->newMainWindow(); + BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); + return mainWindow->currentTab()->page(); + } else { + PopupWindow *popup = new PopupWindow; + 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) + +void WebPage::handleUnsupportedContent(QNetworkReply *reply) +{ +#if defined(QWEBENGINEPAGE_UNSUPPORTEDCONTENT) + 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()); + } +} + +void WebPage::proxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth, const QString &proxyHost) +{ + 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()); + } +} + +WebView::WebView(QWidget* parent) + : QWebEngineView(parent) + , m_progress(0) + , m_page(new WebPage(this)) +{ + setPage(m_page); + connect(page(), SIGNAL(statusBarMessage(QString)), + SLOT(setStatusBarText(QString))); + connect(this, SIGNAL(loadProgress(int)), + this, SLOT(setProgress(int))); + connect(this, SIGNAL(loadFinished(bool)), + this, SLOT(loadFinished())); + connect(page(), SIGNAL(loadingUrl(QUrl)), + this, SIGNAL(urlChanged(QUrl))); + connect(page(), SIGNAL(downloadRequested(QNetworkRequest)), + this, SLOT(downloadRequested(QNetworkRequest))); +#if defined(QWEBENGINEPAGE_UNSUPPORTEDCONTENT) + page()->setForwardUnsupportedContent(true); +#endif + +} + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ +#if defined(QWEBENGINEPAGE_HITTESTCONTENT) + QWebEngineHitTestResult r = page()->hitTestContent(event->pos()); + if (!r.linkUrl().isEmpty()) { + QMenu menu(this); + menu.addAction(pageAction(QWebEnginePage::OpenLinkInNewWindow)); + menu.addAction(tr("Open in New Tab"), this, SLOT(openLinkInNewTab())); + menu.addSeparator(); + menu.addAction(pageAction(QWebEnginePage::DownloadLinkToDisk)); + // Add link to bookmarks... + menu.addSeparator(); + menu.addAction(pageAction(QWebEnginePage::CopyLinkToClipboard)); + if (page()->settings()->testAttribute(QWebEngineSettings::DeveloperExtrasEnabled)) + menu.addAction(pageAction(QWebEnginePage::InspectElement)); + menu.exec(mapToGlobal(event->pos())); + return; + } +#endif + QWebEngineView::contextMenuEvent(event); +} + +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() +{ + m_page->m_openInNewTab = true; + pageAction(QWebEnginePage::OpenLinkInNewWindow)->trigger(); +} + +void WebView::setProgress(int progress) +{ + m_progress = progress; +} + +void WebView::loadFinished() +{ + if (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; +} + +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; +} + +void WebView::downloadRequested(const QNetworkRequest &request) +{ + BrowserApplication::downloadManager()->download(request); +} |