/**************************************************************************** ** ** 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 "featurepermissionbar.h" #include "networkaccessmanager.h" #include "ui_passworddialog.h" #include "ui_proxy.h" #include "tabwidget.h" #include "webview.h" #include #include #include #include #include #if defined(QWEBENGINEPAGE_HITTESTCONTENT) #include #endif #ifndef QT_NO_UITOOLS #include #endif //QT_NO_UITOOLS #include #include 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 #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(w)) return mw; w = w->parent(); } return BrowserApplication::instance()->mainWindow(); } #if defined(QWEBENGINEPAGE_ACCEPTNAVIGATIONREQUEST) bool WebPage::acceptNavigationRequest(QWebEngineFrame *frame, const QNetworkRequest &request, NavigationType type) { // 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 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() : 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_openInNewTab || type == QWebEnginePage::WebBrowserTab) { m_openInNewTab = false; 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; 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 frames; frames.append(mainFrame()); while (!frames.isEmpty()) { QWebEngineFrame *frame = frames.takeFirst(); if (frame->url() == reply->url()) { frame->setHtml(html, reply->url()); return; } QList 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("Enter username and password for \"%1\" at %2"); 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) { 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("Connect to proxy \"%1\" using:"); 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)) , m_iconReply(0) { setPage(m_page); #if defined(QWEBENGINEPAGE_STATUSBARMESSAGE) connect(page(), SIGNAL(statusBarMessage(QString)), SLOT(setStatusBarText(QString))); #endif connect(this, SIGNAL(loadProgress(int)), this, SLOT(setProgress(int))); connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); connect(page(), SIGNAL(loadingUrl(QUrl)), this, SIGNAL(urlChanged(QUrl))); connect(page(), SIGNAL(iconUrlChanged(QUrl)), this, SLOT(onIconUrlChanged(QUrl))); #if defined(QWEBENGINEPAGE_DOWNLOADREQUESTED) connect(page(), SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest))); #endif connect(page(), &WebPage::featurePermissionRequested, this, &WebView::onFeaturePermissionRequested); #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() { #if defined(QWEBENGINEPAGE_WEBACTION_OPENLINKINNEWWINDOW) m_page->m_openInNewTab = true; pageAction(QWebEnginePage::OpenLinkInNewWindow)->trigger(); #endif } 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; } void WebView::downloadRequested(const QNetworkRequest &request) { BrowserApplication::downloadManager()->download(request); }