1
mirror of https://invent.kde.org/network/falkon.git synced 2024-09-23 10:42:11 +02:00
falkonOfficial/src/lib/webkit/webpage.cpp

1272 lines
40 KiB
C++
Raw Normal View History

2011-03-03 18:29:20 +01:00
/* ============================================================
* QupZilla - WebKit based browser
2014-01-11 16:11:42 +01:00
* Copyright (C) 2010-2014 David Rosca <nowrep@gmail.com>
2011-03-03 18:29:20 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
2011-03-02 16:57:41 +01:00
#include "webpage.h"
#include "tabbedwebview.h"
#include "browserwindow.h"
#include "pluginproxy.h"
2011-03-02 16:57:41 +01:00
#include "downloadmanager.h"
#include "webpluginfactory.h"
#include "mainapplication.h"
#include "checkboxdialog.h"
#include "widget.h"
#include "qztools.h"
#include "speeddial.h"
#include "autofill.h"
#include "popupwebpage.h"
#include "popupwebview.h"
#include "networkmanagerproxy.h"
#include "adblockicon.h"
#include "adblockmanager.h"
#include "iconprovider.h"
#include "qzsettings.h"
#include "useragentmanager.h"
#include "delayedfilewatcher.h"
#include "recoverywidget.h"
#include "searchenginesmanager.h"
#include "html5permissions/html5permissionsmanager.h"
#include "schemehandlers/fileschemehandler.h"
#include "javascript/externaljsobject.h"
2011-03-02 16:57:41 +01:00
#ifdef NONBLOCK_JS_DIALOGS
#include "ui_jsconfirm.h"
#include "ui_jsalert.h"
#include "ui_jsprompt.h"
#include <QPushButton>
#endif
#include <QAuthenticator>
#include <QDir>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
2012-08-31 15:19:07 +02:00
#include <QMouseEvent>
#include <QWebEngineHistory>
#include <QTimer>
#include <QNetworkReply>
#include <QDesktopServices>
#include <QMessageBox>
#include <QFileDialog>
#include <QCheckBox>
2012-04-17 18:26:01 +02:00
QString WebPage::s_lastUploadLocation = QDir::homePath();
QUrl WebPage::s_lastUnsupportedUrl;
QTime WebPage::s_lastUnsupportedUrlTime;
2012-04-17 18:26:01 +02:00
QList<WebPage*> WebPage::s_livingPages;
WebPage::WebPage(QObject* parent)
: QWebEnginePage(parent)
, m_view(0)
, m_fileWatcher(0)
, m_runningLoop(0)
, m_loadProgress(-1)
, m_blockAlerts(false)
, m_secureStatus(false)
, m_adjustingScheduled(false)
2011-03-02 16:57:41 +01:00
{
#if QTWEBENGINE_DISABLED
m_javaScriptEnabled = QWebEngineSettings::globalSettings()->testAttribute(QWebEngineSettings::JavascriptEnabled);
m_networkProxy = new NetworkManagerProxy(this);
m_networkProxy->setPrimaryNetworkAccessManager(mApp->networkManager());
m_networkProxy->setPage(this);
setNetworkAccessManager(m_networkProxy);
2011-03-02 16:57:41 +01:00
setForwardUnsupportedContent(true);
setPluginFactory(new WebPluginFactory(this));
history()->setMaximumItemCount(20);
connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(handleUnsupportedContent(QNetworkReply*)));
connect(this, SIGNAL(loadProgress(int)), this, SLOT(progress(int)));
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(finished()));
connect(this, SIGNAL(printRequested(QWebFrame*)), this, SLOT(printFrame(QWebFrame*)));
connect(this, SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest)));
connect(this, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested()));
frameCreated(mainFrame());
connect(this, SIGNAL(frameCreated(QWebFrame*)), this, SLOT(frameCreated(QWebFrame*)));
connect(this, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)),
this, SLOT(dbQuotaExceeded(QWebFrame*)));
connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJavaScriptObject()));
#if QTWEBKIT_FROM_2_2
connect(this, SIGNAL(featurePermissionRequested(QWebFrame*,QWebPage::Feature)),
this, SLOT(featurePermissionRequested(QWebFrame*,QWebPage::Feature)));
#endif
#if QTWEBKIT_FROM_2_3
connect(this, SIGNAL(applicationCacheQuotaExceeded(QWebSecurityOrigin*,quint64,quint64)),
this, SLOT(appCacheQuotaExceeded(QWebSecurityOrigin*,quint64)));
#elif QTWEBKIT_FROM_2_2
connect(this, SIGNAL(applicationCacheQuotaExceeded(QWebSecurityOrigin*,quint64)),
this, SLOT(appCacheQuotaExceeded(QWebSecurityOrigin*,quint64)));
#endif
#endif
2012-04-17 18:26:01 +02:00
s_livingPages.append(this);
}
WebPage::~WebPage()
{
mApp->plugins()->emitWebPageDeleted(this);
if (m_runningLoop) {
m_runningLoop->exit(1);
m_runningLoop = 0;
}
s_livingPages.removeOne(this);
#if QTWEBENGINE_DISABLED
// Page's network manager will be deleted and then set to null
// Fixes issue with network manager being used after deleted in destructor
setNetworkAccessManager(0);
#endif
}
void WebPage::setWebView(TabbedWebView* view)
{
if (m_view == view) {
return;
}
if (m_view) {
delete m_view;
m_view = 0;
}
m_view = view;
m_view->setWebPage(this);
connect(m_view, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl)));
}
void WebPage::scheduleAdjustPage()
{
WebView* webView = qobject_cast<WebView*>(view());
if (!webView) {
return;
}
if (webView->isLoading()) {
m_adjustingScheduled = true;
}
else {
const QSize originalSize = webView->size();
QSize newSize(originalSize.width() - 1, originalSize.height() - 1);
webView->resize(newSize);
webView->resize(originalSize);
}
}
bool WebPage::loadingError() const
{
#if QTWEBENGINE_DISABLED
return !mainFrame()->findFirstElement("span[id=\"qupzilla-error-page\"]").isNull();
#else
return false;
#endif
}
void WebPage::addRejectedCerts(const QList<QSslCertificate> &certs)
{
foreach (const QSslCertificate &cert, certs) {
if (!m_rejectedSslCerts.contains(cert)) {
m_rejectedSslCerts.append(cert);
}
}
}
bool WebPage::containsRejectedCerts(const QList<QSslCertificate> &certs)
{
int matches = 0;
foreach (const QSslCertificate &cert, certs) {
if (m_rejectedSslCerts.contains(cert)) {
++matches;
}
if (m_sslCert == cert) {
m_sslCert.clear();
}
}
return matches == certs.count();
}
#if QTWEBENGINE_DISABLED
QWebElement WebPage::activeElement() const
{
QRect activeRect = inputMethodQuery(Qt::ImMicroFocus).toRect();
return mainFrame()->hitTestContent(activeRect.center()).element();
}
#endif
bool WebPage::isRunningLoop()
{
return m_runningLoop;
}
bool WebPage::isLoading() const
{
return m_loadProgress < 100;
}
void WebPage::urlChanged(const QUrl &url)
{
#if QTWEBENGINE_DISABLED
// Make sure JavaScript is enabled for qupzilla pages regardless of user settings
if (url.scheme() == QLatin1String("qupzilla")) {
settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
}
#endif
if (isLoading()) {
m_adBlockedEntries.clear();
m_blockAlerts = false;
}
}
void WebPage::progress(int prog)
{
m_loadProgress = prog;
bool secStatus = QzTools::isCertificateValid(sslCertificate());
if (secStatus != m_secureStatus) {
m_secureStatus = secStatus;
emit privacyChanged(QzTools::isCertificateValid(sslCertificate()));
}
2011-03-02 16:57:41 +01:00
}
void WebPage::finished()
{
progress(100);
if (m_adjustingScheduled) {
m_adjustingScheduled = false;
setZoomFactor(zoomFactor() + 1);
setZoomFactor(zoomFactor() - 1);
}
// File scheme watcher
if (url().scheme() == QLatin1String("file")) {
QFileInfo info(url().toLocalFile());
if (info.isFile()) {
if (!m_fileWatcher) {
m_fileWatcher = new DelayedFileWatcher(this);
connect(m_fileWatcher, SIGNAL(delayedFileChanged(QString)), this, SLOT(watchedFileChanged(QString)));
}
const QString filePath = url().toLocalFile();
if (QFile::exists(filePath) && !m_fileWatcher->files().contains(filePath)) {
m_fileWatcher->addPath(filePath);
}
}
}
else if (m_fileWatcher && !m_fileWatcher->files().isEmpty()) {
m_fileWatcher->removePaths(m_fileWatcher->files());
}
// AdBlock
cleanBlockedObjects();
}
void WebPage::watchedFileChanged(const QString &file)
{
if (url().toLocalFile() == file) {
triggerAction(QWebEnginePage::Reload);
}
}
#if QTWEBENGINE_DISABLED
void WebPage::printFrame(QWebEngineFrame* frame)
{
WebView* webView = qobject_cast<WebView*>(view());
if (!webView) {
return;
}
webView->printPage(frame);
}
#endif
void WebPage::addJavaScriptObject()
{
#if QTWEBENGINE_DISABLED
// Make sure all other sites have JavaScript set by user preferences
// (JavaScript is enabled in WebPage::urlChanged)
if (url().scheme() != QLatin1String("qupzilla")) {
settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, m_javaScriptEnabled);
}
ExternalJsObject* jsObject = new ExternalJsObject(this);
addToJavaScriptWindowObject("external", jsObject);
if (url().toString() == QLatin1String("qupzilla:speeddial")) {
jsObject->setOnSpeedDial(true);
mApp->plugins()->speedDial()->addWebFrame(mainFrame());
}
#endif
}
2011-03-02 16:57:41 +01:00
void WebPage::handleUnsupportedContent(QNetworkReply* reply)
{
if (!reply) {
2011-03-02 16:57:41 +01:00
return;
}
const QUrl url = reply->url();
2011-03-02 16:57:41 +01:00
switch (reply->error()) {
2011-03-02 16:57:41 +01:00
case QNetworkReply::NoError:
if (reply->header(QNetworkRequest::ContentTypeHeader).isValid()) {
QString requestUrl = reply->request().url().toString(QUrl::RemoveFragment | QUrl::RemoveQuery);
#if QTWEBENGINE_DISABLED
if (requestUrl.endsWith(QLatin1String(".swf"))) {
const QWebElement docElement = mainFrame()->documentElement();
const QWebElement object = docElement.findFirst(QString("object[src=\"%1\"]").arg(requestUrl));
const QWebElement embed = docElement.findFirst(QString("embed[src=\"%1\"]").arg(requestUrl));
if (!object.isNull() || !embed.isNull()) {
qDebug() << "WebPage::UnsupportedContent" << url << "Attempt to download flash object on site!";
reply->deleteLater();
return;
}
}
DownloadManager* dManager = mApp->downloadManager();
dManager->handleUnsupportedContent(reply, this);
#endif
2011-03-02 16:57:41 +01:00
return;
}
// Falling unsupported content with invalid ContentTypeHeader to be handled as UnknownProtocol
case QNetworkReply::ProtocolUnknownError: {
if (url.scheme() == QLatin1String("file")) {
FileSchemeHandler::handleUrl(url);
return;
}
if (url.scheme() == QLatin1String("ftp")) {
DownloadManager* dManager = mApp->downloadManager();
#if QTWEBENGINE_DISABLED
dManager->handleUnsupportedContent(reply, this);
#endif
return;
}
qDebug() << "WebPage::UnsupportedContent" << url << "ProtocolUnknowError";
desktopServicesOpen(url);
reply->deleteLater();
2011-03-02 16:57:41 +01:00
return;
}
2011-03-02 16:57:41 +01:00
default:
break;
}
qDebug() << "WebPage::UnsupportedContent error" << url << reply->errorString();
reply->deleteLater();
2011-03-02 16:57:41 +01:00
}
void WebPage::handleUnknownProtocol(const QUrl &url)
{
const QString protocol = url.scheme();
if (protocol == QLatin1String("mailto")) {
desktopServicesOpen(url);
return;
}
if (qzSettings->blockedProtocols.contains(protocol)) {
qDebug() << "WebPage::handleUnknownProtocol Protocol" << protocol << "is blocked!";
return;
}
if (qzSettings->autoOpenProtocols.contains(protocol)) {
desktopServicesOpen(url);
return;
}
CheckBoxDialog dialog(QDialogButtonBox::Yes | QDialogButtonBox::No, view());
const QString wrappedUrl = QzTools::alignTextToWidth(url.toString(), "<br/>", dialog.fontMetrics(), 450);
const QString text = tr("QupZilla cannot handle <b>%1:</b> links. The requested link "
"is <ul><li>%2</li></ul>Do you want QupZilla to try "
"open this link in system application?").arg(protocol, wrappedUrl);
dialog.setText(text);
dialog.setCheckBoxText(tr("Remember my choice for this protocol"));
dialog.setWindowTitle(tr("External Protocol Request"));
dialog.setIcon(IconProvider::standardIcon(QStyle::SP_MessageBoxQuestion));
switch (dialog.exec()) {
case QDialog::Accepted:
if (dialog.isChecked()) {
qzSettings->autoOpenProtocols.append(protocol);
qzSettings->saveSettings();
}
2014-03-03 12:55:35 +01:00
QDesktopServices::openUrl(url);
break;
case QDialog::Rejected:
if (dialog.isChecked()) {
qzSettings->blockedProtocols.append(protocol);
qzSettings->saveSettings();
}
break;
default:
break;
}
}
void WebPage::desktopServicesOpen(const QUrl &url)
{
// Open same url only once in 2 secs
2014-03-03 12:55:35 +01:00
const int sameUrlTimeout = 2 * 1000;
2014-03-03 12:55:35 +01:00
if (s_lastUnsupportedUrl != url || s_lastUnsupportedUrlTime.isNull() || s_lastUnsupportedUrlTime.elapsed() > sameUrlTimeout) {
s_lastUnsupportedUrl = url;
2014-03-03 12:55:35 +01:00
s_lastUnsupportedUrlTime.restart();
QDesktopServices::openUrl(url);
}
else {
qWarning() << "WebPage::desktopServicesOpen Url" << url << "has already been opened!\n"
"Ignoring it to prevent infinite loop!";
}
}
void WebPage::downloadRequested(const QNetworkRequest &request)
{
DownloadManager* dManager = mApp->downloadManager();
dManager->download(request, this);
}
void WebPage::windowCloseRequested()
{
WebView* webView = qobject_cast<WebView*>(view());
if (!webView) {
return;
}
webView->closeView();
}
#if QTWEBENGINE_DISABLED
2014-09-26 09:53:45 +02:00
void WebPage::frameCreated(QWebFrame* frame)
{
connect(frame, SIGNAL(initialLayoutCompleted()), this, SLOT(frameInitialLayoutCompleted()));
}
void WebPage::frameInitialLayoutCompleted()
{
QWebFrame* frame = qobject_cast<QWebFrame*>(sender());
if (!frame)
return;
// Autofill
m_passwordEntries = mApp->autoFill()->completeFrame(frame);
}
#endif
void WebPage::authentication(const QUrl &requestUrl, QAuthenticator* auth)
{
QDialog* dialog = new QDialog();
dialog->setWindowTitle(tr("Authorisation required"));
QFormLayout* formLa = new QFormLayout(dialog);
QLabel* label = new QLabel(dialog);
QLabel* userLab = new QLabel(dialog);
QLabel* passLab = new QLabel(dialog);
userLab->setText(tr("Username: "));
passLab->setText(tr("Password: "));
QLineEdit* user = new QLineEdit(dialog);
QLineEdit* pass = new QLineEdit(dialog);
pass->setEchoMode(QLineEdit::Password);
QCheckBox* save = new QCheckBox(dialog);
save->setText(tr("Save username and password on this site"));
QDialogButtonBox* box = new QDialogButtonBox(dialog);
box->addButton(QDialogButtonBox::Ok);
box->addButton(QDialogButtonBox::Cancel);
connect(box, SIGNAL(rejected()), dialog, SLOT(reject()));
connect(box, SIGNAL(accepted()), dialog, SLOT(accept()));
label->setText(tr("A username and password are being requested by %1. "
"The site says: \"%2\"").arg(requestUrl.host(), QzTools::escape(auth->realm())));
formLa->addRow(label);
formLa->addRow(userLab, user);
formLa->addRow(passLab, pass);
formLa->addRow(save);
formLa->addWidget(box);
AutoFill* fill = mApp->autoFill();
QString storedUser;
QString storedPassword;
bool shouldUpdateEntry = false;
if (fill->isStored(requestUrl)) {
const QVector<PasswordEntry> &data = fill->getFormData(requestUrl);
if (!data.isEmpty()) {
save->setChecked(true);
shouldUpdateEntry = true;
storedUser = data.first().username;
storedPassword = data.first().password;
user->setText(storedUser);
pass->setText(storedPassword);
}
}
// Try to set the originating WebTab as a current tab
TabbedWebView* tabView = qobject_cast<TabbedWebView*>(view());
if (tabView) {
tabView->setAsCurrentTab();
}
// Do not save when private browsing is enabled
if (mApp->isPrivate()) {
save->setVisible(false);
}
if (dialog->exec() != QDialog::Accepted) {
return;
}
auth->setUser(user->text());
auth->setPassword(pass->text());
if (save->isChecked()) {
if (shouldUpdateEntry) {
if (storedUser != user->text() || storedPassword != pass->text()) {
fill->updateEntry(requestUrl, user->text(), pass->text());
}
}
else {
fill->addEntry(requestUrl, user->text(), pass->text());
}
}
}
void WebPage::proxyAuthentication(const QUrl &requestUrl, QAuthenticator* auth, const QString &proxyHost)
{
Q_UNUSED(requestUrl)
QVector<PasswordEntry> passwords = mApp->autoFill()->getFormData(QUrl(proxyHost));
if (!passwords.isEmpty()) {
auth->setUser(passwords.at(0).username);
auth->setPassword(passwords.at(0).password);
return;
}
QDialog* dialog = new QDialog();
dialog->setWindowTitle(tr("Proxy authorisation required"));
QFormLayout* formLa = new QFormLayout(dialog);
QLabel* label = new QLabel(dialog);
QLabel* userLab = new QLabel(dialog);
QLabel* passLab = new QLabel(dialog);
userLab->setText(tr("Username: "));
passLab->setText(tr("Password: "));
QLineEdit* user = new QLineEdit(dialog);
QLineEdit* pass = new QLineEdit(dialog);
pass->setEchoMode(QLineEdit::Password);
QCheckBox* save = new QCheckBox(dialog);
save->setText(tr("Remember username and password for this proxy."));
QDialogButtonBox* box = new QDialogButtonBox(dialog);
box->addButton(QDialogButtonBox::Ok);
box->addButton(QDialogButtonBox::Cancel);
connect(box, SIGNAL(rejected()), dialog, SLOT(reject()));
connect(box, SIGNAL(accepted()), dialog, SLOT(accept()));
label->setText(tr("A username and password are being requested by proxy %1. ").arg(proxyHost));
formLa->addRow(label);
formLa->addRow(userLab, user);
formLa->addRow(passLab, pass);
formLa->addRow(save);
formLa->addWidget(box);
if (dialog->exec() != QDialog::Accepted) {
return;
}
if (save->isChecked()) {
mApp->autoFill()->addEntry(QUrl(proxyHost), user->text(), pass->text());
}
auth->setUser(user->text());
auth->setPassword(pass->text());
}
#if QTWEBENGINE_DISABLED
void WebPage::dbQuotaExceeded(QWebEngineFrame* frame)
{
if (!frame) {
return;
}
const QWebSecurityOrigin origin = frame->securityOrigin();
const qint64 oldQuota = origin.databaseQuota();
frame->securityOrigin().setDatabaseQuota(oldQuota * 2);
}
#endif
void WebPage::doWebSearch(const QString &text)
{
WebView* webView = qobject_cast<WebView*>(view());
if (webView) {
const LoadRequest searchRequest = mApp->searchEnginesManager()->searchResult(text);
webView->load(searchRequest);
}
}
#ifdef USE_QTWEBKIT_2_2
void WebPage::appCacheQuotaExceeded(QWebSecurityOrigin* origin, quint64 originalQuota)
{
if (!origin) {
return;
}
origin->setApplicationCacheQuota(originalQuota * 2);
}
void WebPage::featurePermissionRequested(QWebEngineFrame* frame, const QWebEnginePage::Feature &feature)
{
mApp->html5PermissionsManager()->requestPermissions(this, frame, feature);
}
#endif // USE_QTWEBKIT_2_2
bool WebPage::event(QEvent* event)
{
if (event->type() == QEvent::Leave) {
// QWebEnginePagePrivate::leaveEvent():
// Fake a mouse move event just outside of the widget, since all
// the interesting mouse-out behavior like invalidating scrollbars
// is handled by the WebKit event handler's mouseMoved function.
// However, its implementation fake mouse move event on QCursor::pos()
// position that is in global screen coordinates. So instead of
// really faking it, it just creates mouse move event somewhere in
// page. It can for example focus a link, and then link url gets
// stuck in status bar message.
// So we are faking mouse move event with proper coordinates for
// so called "just outside of the widget" position
const QPoint cursorPos = view()->mapFromGlobal(QCursor::pos());
QPoint mousePos;
if (cursorPos.y() < 0) {
// Left on top
mousePos = QPoint(cursorPos.x(), -1);
}
else if (cursorPos.x() < 0) {
// Left on left
mousePos = QPoint(-1, cursorPos.y());
}
else if (cursorPos.y() > view()->height()) {
// Left on bottom
mousePos = QPoint(cursorPos.x(), view()->height() + 1);
}
else {
// Left on right
mousePos = QPoint(view()->width() + 1, cursorPos.y());
}
QMouseEvent fakeEvent(QEvent::MouseMove, mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
return QWebEnginePage::event(&fakeEvent);
}
return QWebEnginePage::event(event);
}
2011-04-24 09:08:53 +02:00
void WebPage::setSSLCertificate(const QSslCertificate &cert)
{
// if (cert != m_SslCert)
m_sslCert = cert;
}
QSslCertificate WebPage::sslCertificate()
{
if (url().scheme() == QLatin1String("https") && QzTools::isCertificateValid(m_sslCert)) {
return m_sslCert;
}
return QSslCertificate();
}
#if QTWEBENGINE_DISABLED
bool WebPage::acceptNavigationRequest(QWebEngineFrame* frame, const QNetworkRequest &request, NavigationType type)
2011-03-02 16:57:41 +01:00
{
m_lastRequestType = type;
m_lastRequestUrl = request.url();
if (type == QWebEnginePage::NavigationTypeFormResubmitted) {
// Don't show this dialog if app is still starting
if (!view() || !view()->isVisible()) {
return false;
}
QString message = tr("To display this page, QupZilla must resend the request \n"
"(such as a search or order confirmation) that was performed earlier.");
bool result = (QMessageBox::question(view(), tr("Confirm form resubmission"),
message, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes);
if (!result) {
2011-03-02 16:57:41 +01:00
return false;
}
2011-03-02 16:57:41 +01:00
}
bool accept = QWebEnginePage::acceptNavigationRequest(frame, request, type);
2011-03-02 16:57:41 +01:00
return accept;
}
#endif
2011-03-02 16:57:41 +01:00
void WebPage::populateNetworkRequest(QNetworkRequest &request)
{
WebPage* pagePointer = this;
QVariant variant = QVariant::fromValue((void*) pagePointer);
2011-03-02 16:57:41 +01:00
request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 100), variant);
#if QTWEBENGINE_DISABLED
if (m_lastRequestUrl == request.url()) {
request.setAttribute((QNetworkRequest::Attribute)(QNetworkRequest::User + 101), m_lastRequestType);
if (m_lastRequestType == NavigationTypeLinkClicked) {
request.setRawHeader("X-QupZilla-UserLoadAction", QByteArray("1"));
}
}
#endif
2011-03-02 16:57:41 +01:00
}
QWebEnginePage* WebPage::createWindow(QWebEnginePage::WebWindowType type)
2011-03-02 16:57:41 +01:00
{
if (m_view) {
return new PopupWebPage(type, m_view->browserWindow());
}
if (PopupWebPage* popupPage = qobject_cast<PopupWebPage*>(this)) {
return new PopupWebPage(type, popupPage->mainWindow());
}
return 0;
2011-03-02 16:57:41 +01:00
}
QObject* WebPage::createPlugin(const QString &classid, const QUrl &url,
const QStringList &paramNames, const QStringList &paramValues)
{
Q_UNUSED(url)
Q_UNUSED(paramNames)
Q_UNUSED(paramValues)
if (classid == QLatin1String("RecoveryWidget") && mApp->restoreManager() && m_view) {
return new RecoveryWidget(m_view, m_view->browserWindow());
}
else {
load(QUrl("qupzilla:start"));
}
return 0;
}
void WebPage::addAdBlockRule(const AdBlockRule* rule, const QUrl &url)
{
AdBlockedEntry entry;
entry.rule = rule;
entry.url = url;
if (!m_adBlockedEntries.contains(entry)) {
m_adBlockedEntries.append(entry);
}
}
QVector<WebPage::AdBlockedEntry> WebPage::adBlockedEntries() const
{
return m_adBlockedEntries;
}
bool WebPage::hasMultipleUsernames() const
{
return m_passwordEntries.count() > 1;
}
QVector<PasswordEntry> WebPage::autoFillData() const
{
return m_passwordEntries;
}
void WebPage::cleanBlockedObjects()
{
#if QTWEBENGINE_DISABLED
AdBlockManager* manager = AdBlockManager::instance();
if (!manager->isEnabled()) {
return;
}
const QWebElement docElement = mainFrame()->documentElement();
foreach (const AdBlockedEntry &entry, m_adBlockedEntries) {
const QString urlString = entry.url.toString();
if (urlString.endsWith(QLatin1String(".js")) || urlString.endsWith(QLatin1String(".css"))) {
continue;
}
QString urlEnd;
int pos = urlString.lastIndexOf(QLatin1Char('/'));
if (pos > 8) {
urlEnd = urlString.mid(pos + 1);
}
if (urlString.endsWith(QLatin1Char('/'))) {
urlEnd = urlString.left(urlString.size() - 1);
}
QString selector("img[src$=\"%1\"], iframe[src$=\"%1\"],embed[src$=\"%1\"]");
QWebElementCollection elements = docElement.findAll(selector.arg(urlEnd));
foreach (QWebElement element, elements) {
QString src = element.attribute("src");
src.remove(QLatin1String("../"));
if (urlString.contains(src)) {
element.setStyleProperty("display", "none");
}
}
}
// Apply domain-specific element hiding rules
QString elementHiding = manager->elementHidingRulesForDomain(url());
if (elementHiding.isEmpty()) {
return;
}
elementHiding.append(QLatin1String("\n</style>"));
QWebElement bodyElement = docElement.findFirst("body");
bodyElement.appendInside("<style type=\"text/css\">\n/* AdBlock for QupZilla */\n" + elementHiding);
// When hiding some elements, scroll position of page will change
// If user loaded anchor link in background tab (and didn't show it yet), fix the scroll position
if (view() && !view()->isVisible() && !url().fragment().isEmpty()) {
mainFrame()->scrollToAnchor(url().fragment());
}
#endif
}
QString WebPage::userAgentForUrl(const QUrl &url) const
{
QString userAgent = mApp->userAgentManager()->userAgentForUrl(url);
#if QTWEBENGINE_DISABLED
if (userAgent.isEmpty()) {
userAgent = QWebEnginePage::userAgentForUrl(url);
#ifdef Q_OS_MAC
#ifdef __i386__ || __x86_64__
userAgent.replace(QLatin1String("PPC Mac OS X"), QLatin1String("Intel Mac OS X"));
#endif
#endif
}
#endif
return userAgent;
}
#if QTWEBENGINE_DISABLED
bool WebPage::supportsExtension(Extension extension) const
{
Q_UNUSED(extension)
return true;
}
bool WebPage::extension(Extension extension, const ExtensionOption* option, ExtensionReturn* output)
2011-03-02 16:57:41 +01:00
{
if (extension == ChooseMultipleFilesExtension) {
const QWebEnginePage::ChooseMultipleFilesExtensionOption* exOption = static_cast<const QWebEnginePage::ChooseMultipleFilesExtensionOption*>(option);
QWebEnginePage::ChooseMultipleFilesExtensionReturn* exReturn = static_cast<QWebEnginePage::ChooseMultipleFilesExtensionReturn*>(output);
if (!exOption || !exReturn) {
return QWebEnginePage::extension(extension, option, output);
}
QString suggestedFileName;
if (!exOption->suggestedFileNames.isEmpty()) {
suggestedFileName = exOption->suggestedFileNames.at(0);
}
exReturn->fileNames = QzTools::getOpenFileNames("WebPage-UploadFiles", 0, tr("Select files to upload..."), suggestedFileName);
return true;
}
2011-03-02 16:57:41 +01:00
const ErrorPageExtensionOption* exOption = static_cast<const QWebEnginePage::ErrorPageExtensionOption*>(option);
ErrorPageExtensionReturn* exReturn = static_cast<QWebEnginePage::ErrorPageExtensionReturn*>(output);
if (!exOption || !exReturn) {
return QWebEnginePage::extension(extension, option, output);
}
WebPage* erPage = qobject_cast<WebPage*>(exOption->frame->page());
if (!erPage) {
return QWebEnginePage::extension(extension, option, output);
}
2011-03-02 16:57:41 +01:00
QString errorString;
if (exOption->domain == QWebEnginePage::QtNetwork) {
2011-03-02 16:57:41 +01:00
switch (exOption->error) {
case QNetworkReply::ConnectionRefusedError:
errorString = tr("Server refused the connection");
break;
case QNetworkReply::RemoteHostClosedError:
errorString = tr("Server closed the connection");
break;
case QNetworkReply::HostNotFoundError:
// If a one-word host was not find, search for the text instead
// It needs to be async to correctly refresh loading state
if (!exOption->url.host().isEmpty() && !exOption->url.host().contains(QL1C('.'))) {
const QString text = QzTools::fromPunycode(exOption->url.host().toUtf8());
QMetaObject::invokeMethod(this, "doWebSearch", Qt::QueuedConnection, Q_ARG(QString, text));
return false;
}
2011-03-02 16:57:41 +01:00
errorString = tr("Server not found");
break;
case QNetworkReply::TimeoutError:
errorString = tr("Connection timed out");
break;
case QNetworkReply::SslHandshakeFailedError:
errorString = tr("Untrusted connection");
break;
case QNetworkReply::TemporaryNetworkFailureError:
errorString = tr("Temporary network failure");
break;
case QNetworkReply::ProxyConnectionRefusedError:
errorString = tr("Proxy connection refused");
break;
case QNetworkReply::ProxyNotFoundError:
errorString = tr("Proxy server not found");
break;
case QNetworkReply::ProxyTimeoutError:
errorString = tr("Proxy connection timed out");
break;
case QNetworkReply::ProxyAuthenticationRequiredError:
errorString = tr("Proxy authentication required");
break;
case QNetworkReply::ContentNotFoundError:
errorString = tr("Content not found");
break;
case QNetworkReply::UnknownNetworkError:
errorString = exOption->errorString.isEmpty() ? tr("Unknown network error") : exOption->errorString;
break;
case QNetworkReply::ProtocolUnknownError: {
// Sometimes exOption->url returns just "?" instead of actual url
const QUrl unknownProtocolUrl = (exOption->url.toString() == QLatin1String("?")) ? erPage->mainFrame()->requestedUrl() : exOption->url;
handleUnknownProtocol(unknownProtocolUrl);
return false;
}
case QNetworkReply::ContentAccessDenied:
if (exOption->errorString.startsWith(QLatin1String("AdBlock"))) {
if (exOption->frame != erPage->mainFrame()) {
// Content in <iframe>
QWebElement docElement = erPage->mainFrame()->documentElement();
QWebElementCollection elements;
elements.append(docElement.findAll(QSL("iframe")));
foreach (QWebElement element, elements) {
const QString src = element.attribute(QSL("src"));
if (!src.isEmpty() && exOption->url.toString().contains(src)) {
element.setStyleProperty(QSL("display"), QSL("none"));
}
}
return false;
}
else {
// The whole page is blocked
QString rule = exOption->errorString;
rule.remove(QLatin1String("AdBlock: "));
QString errString = QzTools::readAllFileContents(":/html/adblockPage.html");
errString.replace(QLatin1String("%TITLE%"), tr("AdBlocked Content"));
errString.replace(QLatin1String("%IMAGE%"), QLatin1String("qrc:html/adblock_big.png"));
errString.replace(QLatin1String("%FAVICON%"), QLatin1String("qrc:html/adblock_big.png"));
errString.replace(QLatin1String("%RULE%"), tr("Blocked by <i>%1</i>").arg(rule));
errString = QzTools::applyDirectionToPage(errString);
exReturn->baseUrl = exOption->url;
exReturn->content = QString(errString + "<span id=\"qupzilla-error-page\"></span>").toUtf8();
if (PopupWebPage* popupPage = qobject_cast<PopupWebPage*>(exOption->frame->page())) {
WebView* view = qobject_cast<WebView*>(popupPage->view());
if (view) {
// Closing blocked popup
popupPage->mainWindow()->adBlockIcon()->popupBlocked(rule, exOption->url);
view->closeView();
}
}
return true;
}
}
errorString = tr("Content Access Denied");
break;
2011-03-02 16:57:41 +01:00
default:
if (exOption->errorString != QLatin1String("QupZilla:No Error")) {
qDebug() << "Content error: " << exOption->errorString << exOption->error;
}
return false;
2011-03-02 16:57:41 +01:00
}
}
else if (exOption->domain == QWebEnginePage::Http) {
// 200 status code = OK
// It shouldn't be reported as an error, but sometimes it is ...
if (exOption->error == 200) {
return false;
}
2011-03-02 16:57:41 +01:00
errorString = tr("Error code %1").arg(exOption->error);
}
else if (exOption->domain == QWebEnginePage::WebKit) {
return false; // Downloads
}
2011-03-02 16:57:41 +01:00
const QUrl loadedUrl = exOption->url;
2011-03-02 16:57:41 +01:00
exReturn->baseUrl = loadedUrl;
QFile file(":/html/errorPage.html");
file.open(QFile::ReadOnly);
QString errString = file.readAll();
errString.replace(QLatin1String("%TITLE%"), tr("Failed loading page"));
2011-03-02 16:57:41 +01:00
errString.replace(QLatin1String("%IMAGE%"), QzTools::pixmapToByteArray(IconProvider::standardIcon(QStyle::SP_MessageBoxWarning).pixmap(45, 45)));
errString.replace(QLatin1String("%FAVICON%"), QzTools::pixmapToByteArray(IconProvider::standardIcon(QStyle::SP_MessageBoxWarning).pixmap(16, 16)));
errString.replace(QLatin1String("%BOX-BORDER%"), QLatin1String("qrc:html/box-border.png"));
2011-03-02 16:57:41 +01:00
QString heading2 = loadedUrl.host().isEmpty() ? tr("QupZilla can't load page.") : tr("QupZilla can't load page from %1.").arg(loadedUrl.host());
errString.replace(QLatin1String("%HEADING%"), errorString);
errString.replace(QLatin1String("%HEADING2%"), heading2);
errString.replace(QLatin1String("%LI-1%"), tr("Check the address for typing errors such as <b>ww.</b>example.com instead of <b>www.</b>example.com"));
errString.replace(QLatin1String("%LI-2%"), tr("If you are unable to load any pages, check your computer's network connection."));
errString.replace(QLatin1String("%LI-3%"), tr("If your computer or network is protected by a firewall or proxy, make sure that QupZilla is permitted to access the Web."));
errString.replace(QLatin1String("%TRY-AGAIN%"), tr("Try Again"));
errString = QzTools::applyDirectionToPage(errString);
2011-03-02 16:57:41 +01:00
exReturn->content = QString(errString + "<span id=\"qupzilla-error-page\"></span>").toUtf8();
2011-03-02 16:57:41 +01:00
return true;
}
#endif
2011-03-02 16:57:41 +01:00
bool WebPage::javaScriptPrompt(QUrl securityOrigin, const QString &msg, const QString &defaultValue, QString* result)
{
#ifndef NONBLOCK_JS_DIALOGS
return QWebEnginePage::javaScriptPrompt(securityOrigin, msg, defaultValue, result);
#else
if (m_runningLoop) {
return false;
}
WebView* webView = qobject_cast<WebView*>(originatingFrame->page()->view());
ResizableFrame* widget = new ResizableFrame(webView->overlayWidget());
widget->setObjectName("jsFrame");
Ui_jsPrompt* ui = new Ui_jsPrompt();
ui->setupUi(widget);
ui->message->setText(msg);
ui->lineEdit->setText(defaultValue);
ui->lineEdit->setFocus();
widget->resize(originatingFrame->page()->viewportSize());
widget->show();
connect(webView, SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize)));
connect(ui->lineEdit, SIGNAL(returnPressed()), ui->buttonBox->button(QDialogButtonBox::Ok), SLOT(animateClick()));
QEventLoop eLoop;
m_runningLoop = &eLoop;
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit()));
if (eLoop.exec() == 1) {
return result;
}
m_runningLoop = 0;
QString x = ui->lineEdit->text();
bool _result = ui->buttonBox->clickedButtonRole() == QDialogButtonBox::AcceptRole;
*result = x;
delete widget;
webView->setFocus();
return _result;
#endif
}
bool WebPage::javaScriptConfirm(QUrl securityOrigin, const QString &msg)
{
#ifndef NONBLOCK_JS_DIALOGS
return QWebEnginePage::javaScriptConfirm(securityOrigin, msg);
#else
if (m_runningLoop) {
return false;
}
WebView* webView = qobject_cast<WebView*>(originatingFrame->page()->view());
ResizableFrame* widget = new ResizableFrame(webView->overlayWidget());
widget->setObjectName("jsFrame");
Ui_jsConfirm* ui = new Ui_jsConfirm();
ui->setupUi(widget);
ui->message->setText(msg);
ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
widget->resize(originatingFrame->page()->viewportSize());
widget->show();
connect(webView, SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize)));
QEventLoop eLoop;
m_runningLoop = &eLoop;
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit()));
if (eLoop.exec() == 1) {
return false;
}
m_runningLoop = 0;
bool result = ui->buttonBox->clickedButtonRole() == QDialogButtonBox::AcceptRole;
delete widget;
webView->setFocus();
return result;
#endif
}
void WebPage::javaScriptAlert(QUrl securityOrigin, const QString &msg)
{
Q_UNUSED(securityOrigin)
if (m_blockAlerts || m_runningLoop) {
return;
}
#ifndef NONBLOCK_JS_DIALOGS
QString title = tr("JavaScript alert");
if (!url().host().isEmpty()) {
title.append(QString(" - %1").arg(url().host()));
}
CheckBoxDialog dialog(QDialogButtonBox::Ok, view());
dialog.setWindowTitle(title);
dialog.setText(msg);
dialog.setCheckBoxText(tr("Prevent this page from creating additional dialogs"));
dialog.setIcon(IconProvider::standardIcon(QStyle::SP_MessageBoxInformation));
dialog.exec();
m_blockAlerts = dialog.isChecked();
#else
WebView* webView = qobject_cast<WebView*>(originatingFrame->page()->view());
ResizableFrame* widget = new ResizableFrame(webView->overlayWidget());
widget->setObjectName("jsFrame");
Ui_jsAlert* ui = new Ui_jsAlert();
ui->setupUi(widget);
ui->message->setText(msg);
ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
widget->resize(originatingFrame->page()->viewportSize());
widget->show();
connect(webView, SIGNAL(viewportResized(QSize)), widget, SLOT(slotResize(QSize)));
QEventLoop eLoop;
m_runningLoop = &eLoop;
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), &eLoop, SLOT(quit()));
if (eLoop.exec() == 1) {
return;
}
m_runningLoop = 0;
m_blockAlerts = ui->preventAlerts->isChecked();
delete widget;
webView->setFocus();
#endif
}
void WebPage::setJavaScriptEnabled(bool enabled)
{
#if QTWEBENGINE_DISABLED
settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, enabled);
m_javaScriptEnabled = enabled;
#endif
}
#if QTWEBENGINE_DISABLED
QString WebPage::chooseFile(QWebEngineFrame* originatingFrame, const QString &oldFile)
{
QString suggFileName;
if (oldFile.isEmpty()) {
2012-04-17 18:26:01 +02:00
suggFileName = s_lastUploadLocation;
}
else {
suggFileName = oldFile;
}
const QString fileName = QzTools::getOpenFileName("WebPage-ChooseFile", view(), tr("Choose file..."), suggFileName);
if (!fileName.isEmpty()) {
2012-04-17 18:26:01 +02:00
s_lastUploadLocation = fileName;
// Check if we can read from file
QFile file(fileName);
if (!file.open(QFile::ReadOnly)) {
const QString msg = tr("Cannot read data from <b>%1</b>. Upload was cancelled!").arg(fileName);
QMessageBox::critical(view(), tr("Cannot read file!"), msg);
return QString();
}
}
return fileName;
}
#endif
bool WebPage::isPointerSafeToUse(WebPage* page)
{
// Pointer to WebPage is passed with every QNetworkRequest casted to void*
// So there is no way to test whether pointer is still valid or not, except
// this hack.
2012-04-17 18:26:01 +02:00
return page == 0 ? false : s_livingPages.contains(page);
}