diff options
author | Andrea Diamantini <adjam7@gmail.com> | 2012-07-31 23:58:06 +0200 |
---|---|---|
committer | Andrea Diamantini <adjam7@gmail.com> | 2012-12-10 02:48:04 +0100 |
commit | b7b5f23f928ef93abff83b6eadede922c34b355b (patch) | |
tree | 53fa667d12922aa3c74f47a61c9041b17e5fdd02 /src/webtab | |
parent | Download Manager (diff) | |
download | rekonq-b7b5f23f928ef93abff83b6eadede922c34b355b.tar.xz |
Copy webtab related files from rekonq repo
NOTE: This change does NOT affect CMakeLists.txt, so that compilation
will continue to work as the previous commit.
I'm just trying to track changes from original rekonq files...
Diffstat (limited to 'src/webtab')
27 files changed, 5477 insertions, 0 deletions
diff --git a/src/webtab/clicktoflash.cpp b/src/webtab/clicktoflash.cpp new file mode 100644 index 00000000..baa61367 --- /dev/null +++ b/src/webtab/clicktoflash.cpp @@ -0,0 +1,135 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Benjamin C. Meyer <ben@meyerhome.net> +* Copyright (C) 2010-2011 by Matthieu Gicquel <matgic78@gmail.com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "clicktoflash.h" +#include "clicktoflash.moc" + +// KDE Includes +#include <KLocalizedString> + +// Qt Includes +#include <QHBoxLayout> +#include <QToolButton> + +#include <QWebElement> +#include <QWebFrame> +#include <QWebView> + + +ClickToFlash::ClickToFlash(const QUrl &pluginUrl, QWidget *parent) + : QWidget(parent) + , m_url(pluginUrl) +{ + QHBoxLayout *l = new QHBoxLayout(this); + setLayout(l); + + QToolButton *button = new QToolButton(this); + button->setPopupMode(QToolButton::InstantPopup); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setText(i18n("Load Plugin")); + button->setAutoRaise(false); + layout()->addWidget(button); + connect(button, SIGNAL(clicked(bool)), this, SLOT(load())); +} + + +void ClickToFlash::load() +{ + QWidget *parent = parentWidget(); + QWebView *view = 0; + while (parent) + { + if (QWebView *aView = qobject_cast<QWebView*>(parent)) + { + view = aView; + break; + } + parent = parent->parentWidget(); + } + if (!view) + return; + + const QString selector = QL1S("%1[type=\"application/x-shockwave-flash\"]"); + + hide(); + + QList<QWebFrame*> frames; + frames.append(view->page()->mainFrame()); + while (!frames.isEmpty()) + { + QWebFrame *frame = frames.takeFirst(); + QWebElement docElement = frame->documentElement(); + + QWebElementCollection elements; + elements.append(docElement.findAll(selector.arg(QL1S("object")))); + elements.append(docElement.findAll(selector.arg(QL1S("embed")))); + + Q_FOREACH(QWebElement element, elements) + { + if (checkElement(element)) + { + QWebElement substitute = element.clone(); + emit signalLoadClickToFlash(true); + element.replace(substitute); + deleteLater(); + return; + } + } + frames += frame->childFrames(); + } +} + + +bool ClickToFlash::checkElement(QWebElement el) +{ + QString checkString; + QString urlString; + + checkString = QUrl(el.attribute("src")).toString(QUrl::RemoveQuery); + urlString = m_url.toString(QUrl::RemoveQuery); + + if (urlString.contains(checkString)) + return true; + + QWebElementCollection collec = el.findAll("*"); + int i = 0; + while (i < collec.count()) + { + QWebElement el = collec.at(i); + + checkString = QUrl(el.attribute("src")).toString(QUrl::RemoveQuery); + urlString = m_url.toString(QUrl::RemoveQuery); + + if (urlString.contains(checkString)) + return true; + + i++; + } + + return false; +} diff --git a/src/webtab/clicktoflash.h b/src/webtab/clicktoflash.h new file mode 100644 index 00000000..fcafea59 --- /dev/null +++ b/src/webtab/clicktoflash.h @@ -0,0 +1,65 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Benjamin C. Meyer <ben@meyerhome.net> +* Copyright (C) 2010-2011 by Matthieu Gicquel <matgic78@gmail.com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef CLICKTOFLASH_H +#define CLICKTOFLASH_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Qt Includes +#include <QUrl> +#include <QWidget> + +// Forward Declarations +class QWebElement; + + +class REKONQ_TESTS_EXPORT ClickToFlash : public QWidget +{ + Q_OBJECT + +public: + explicit ClickToFlash(const QUrl &pluginUrl, QWidget *parent = 0); + +Q_SIGNALS: + void signalLoadClickToFlash(bool); + +private Q_SLOTS: + void load(); + +private: + bool checkElement(QWebElement el); + + /** + used to find the right QWebElement between the ones of the different plugins + */ + const QUrl m_url; +}; + +#endif // CLICKTOFLASH_H diff --git a/src/webtab/messagebar.cpp b/src/webtab/messagebar.cpp new file mode 100644 index 00000000..0e89ac56 --- /dev/null +++ b/src/webtab/messagebar.cpp @@ -0,0 +1,71 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Pierre Rossi <pierre dot rossi at gmail dot com> +* Copyright (C) 2011 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "messagebar.h" +#include "messagebar.moc" + +// KDE Includes +#include <KIcon> +#include <KIconLoader> +#include <KLocalizedString> + +// Qt Includes +#include <QAction> + + +MessageBar::MessageBar(const QString &message, QWidget *parent) + : KMessageWidget(parent) +{ + connect(this, SIGNAL(accepted()), this, SLOT(hideAndDelete())); + connect(this, SIGNAL(rejected()), this, SLOT(hideAndDelete())); + + setMessageType(KMessageWidget::Warning); + + QSize sz = size(); + sz.setWidth(qobject_cast<QWidget *>(parent)->size().width()); + resize(sz); + + setCloseButtonVisible(false); + + setText(message); + + QAction *acceptAction = new QAction(i18n("Yes"), this); + connect(acceptAction, SIGNAL(triggered(bool)), this, SIGNAL(accepted())); + addAction(acceptAction); + + QAction *rejectAction = new QAction(i18n("No"), this); + connect(rejectAction, SIGNAL(triggered(bool)), this, SIGNAL(rejected())); + addAction(rejectAction); +} + + +void MessageBar::hideAndDelete() +{ + animatedHide(); + deleteLater(); +} diff --git a/src/webtab/messagebar.h b/src/webtab/messagebar.h new file mode 100644 index 00000000..bbf5d5d4 --- /dev/null +++ b/src/webtab/messagebar.h @@ -0,0 +1,54 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Pierre Rossi <pierre dot rossi at gmail dot com> +* Copyright (C) 2011 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef MESSAGEBAR_H +#define MESSAGEBAR_H + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KMessageWidget> + + +class REKONQ_TESTS_EXPORT MessageBar : public KMessageWidget +{ + Q_OBJECT + +public: + explicit MessageBar(const QString & message, QWidget *parent); + +private Q_SLOTS: + void hideAndDelete(); + +Q_SIGNALS: + void accepted(); + void rejected(); +}; + + +#endif // MESSAGEBAR_H diff --git a/src/webtab/networkaccessmanager.cpp b/src/webtab/networkaccessmanager.cpp new file mode 100644 index 00000000..dc10da1b --- /dev/null +++ b/src/webtab/networkaccessmanager.cpp @@ -0,0 +1,120 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + + +// Self Includes +#include "networkaccessmanager.h" +#include "networkaccessmanager.moc" + +// Local Includes +#include "adblockmanager.h" +#include "application.h" +#include "webpage.h" + +// KDE Includes +#include <KLocale> +#include <KProtocolManager> + +// Qt Includes +#include <QNetworkReply> +#include <QTimer> + + +class NullNetworkReply : public QNetworkReply +{ +public: + NullNetworkReply(const QNetworkRequest &req, QObject* parent = 0) + : QNetworkReply(parent) + { + setRequest(req); + setUrl(req.url()); + setHeader(QNetworkRequest::ContentLengthHeader, 0); + setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); + setError(QNetworkReply::ContentAccessDenied, i18n("Null reply")); + setAttribute(QNetworkRequest::User, QNetworkReply::ContentAccessDenied); + QTimer::singleShot(0, this, SIGNAL(finished())); + } + + virtual void abort() {} + virtual qint64 bytesAvailable() const + { + return 0; + } + +protected: + virtual qint64 readData(char*, qint64) + { + return -1; + } +}; + + +// ---------------------------------------------------------------------------------------------- + + +NetworkAccessManager::NetworkAccessManager(QObject *parent) + : AccessManager(parent) +{ + QString c = KGlobal::locale()->language(); + + if (c == QL1S("C")) + c = QL1S("en-US"); + else + c = c.replace(QL1C('_') , QL1C('-')); + + c.append(QL1S(", en-US; q=0.8, en; q=0.6")); + + _acceptLanguage = c.toLatin1(); +} + + +QNetworkReply *NetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) +{ + WebPage *parentPage = qobject_cast<WebPage *>(parent()); + + // NOTE: This to get sure we are NOT serving unused requests + if (!parentPage) + return new NullNetworkReply(request, this); + + QNetworkReply *reply = 0; + + // set our "nice" accept-language header... + QNetworkRequest req = request; + req.setRawHeader("Accept-Language", _acceptLanguage); + + // Handle GET operations with AdBlock + if (op == QNetworkAccessManager::GetOperation) + reply = rApp->adblockManager()->block(req, parentPage); + + if (!reply) + reply = AccessManager::createRequest(op, req, outgoingData); + + if (parentPage->hasNetworkAnalyzerEnabled()) + emit networkData(op, req, reply); + + return reply; +} diff --git a/src/webtab/networkaccessmanager.h b/src/webtab/networkaccessmanager.h new file mode 100644 index 00000000..5c877bdf --- /dev/null +++ b/src/webtab/networkaccessmanager.h @@ -0,0 +1,59 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef NETWORKACCESSMANAGER_H +#define NETWORKACCESSMANAGER_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KIO/AccessManager> + +// Qt Includes +#include <QByteArray> + + +class REKONQ_TESTS_EXPORT NetworkAccessManager : public KIO::Integration::AccessManager +{ + Q_OBJECT + +public: + NetworkAccessManager(QObject *parent); + +protected: + virtual QNetworkReply *createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData = 0); + +Q_SIGNALS: + void networkData(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QNetworkReply *reply); + +private: + QByteArray _acceptLanguage; +}; + +#endif // NETWORKACCESSMANAGER_H diff --git a/src/webtab/previewselectorbar.cpp b/src/webtab/previewselectorbar.cpp new file mode 100644 index 00000000..b743e3c9 --- /dev/null +++ b/src/webtab/previewselectorbar.cpp @@ -0,0 +1,132 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "previewselectorbar.h" +#include "previewselectorbar.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Include +#include "application.h" +#include "mainwindow.h" +#include "webpage.h" +#include "webtab.h" +#include "websnap.h" + +// KDE Includes +#include <KIcon> +#include <KLocalizedString> + +// Qt Includes +#include <QAction> + + +PreviewSelectorBar::PreviewSelectorBar(int index, QWidget* parent) + : KMessageWidget(parent) + , m_previewIndex(index) + , m_insertAction(0) +{ + setMessageType(KMessageWidget::Information); + + QSize sz = size(); + sz.setWidth(qobject_cast<QWidget *>(parent)->size().width()); + resize(sz); + + setCloseButtonVisible(false); + + setText(i18n("Please open up the webpage you want to add as favorite")); + + m_insertAction = new QAction(KIcon("insert-image"), i18n("Set to This Page"), this); + connect(m_insertAction, SIGNAL(triggered(bool)), this, SLOT(clicked())); + addAction(m_insertAction); +} + + +void PreviewSelectorBar::verifyUrl() +{ + + if (rApp->mainWindow()->currentTab()->page()->mainFrame()->url().scheme() != "about") + { + m_insertAction->setEnabled(true); + m_insertAction->setToolTip(""); + } + else + { + m_insertAction->setEnabled(false); + m_insertAction->setToolTip(i18n("You cannot add this webpage as favorite")); + } +} + + +void PreviewSelectorBar::loadProgress() +{ + m_insertAction->setEnabled(false); + m_insertAction->setToolTip(i18n("Page is loading...")); +} + + +void PreviewSelectorBar::loadFinished() +{ + m_insertAction->setEnabled(true); + m_insertAction->setToolTip(""); + + verifyUrl(); +} + + +void PreviewSelectorBar::clicked() +{ + WebPage *page = rApp->mainWindow()->currentTab()->page(); + + if (page) + { + KUrl url = page->mainFrame()->url(); + QStringList names = ReKonfig::previewNames(); + QStringList urls = ReKonfig::previewUrls(); + + //cleanup the previous image from the cache (useful to refresh the snapshot) + QFile::remove(WebSnap::imagePathFromUrl(urls.at(m_previewIndex))); + QPixmap preview = WebSnap::renderPagePreview(*page); + preview.save(WebSnap::imagePathFromUrl(url)); + + urls.replace(m_previewIndex, url.toMimeDataString()); + names.replace(m_previewIndex, page->mainFrame()->title()); + + ReKonfig::setPreviewNames(names); + ReKonfig::setPreviewUrls(urls); + + ReKonfig::self()->writeConfig(); + + + page->mainFrame()->load(KUrl("about:favorites")); + } + + animatedHide(); + deleteLater(); +} diff --git a/src/webtab/previewselectorbar.h b/src/webtab/previewselectorbar.h new file mode 100644 index 00000000..fa54303d --- /dev/null +++ b/src/webtab/previewselectorbar.h @@ -0,0 +1,67 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef PREVIEWSELECTORBAR_H +#define PREVIEWSELECTORBAR_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KMessageWidget> + +// Forward Declarations +class QAction; + + +class REKONQ_TESTS_EXPORT PreviewSelectorBar : public KMessageWidget +{ + Q_OBJECT + +public: + PreviewSelectorBar(int index, QWidget *parent); + + inline void setIndex(int index) + { + m_previewIndex = index; + } + +private Q_SLOTS: + void clicked(); + + void loadProgress(); + void loadFinished(); + + void verifyUrl(); + +private: + int m_previewIndex; + QAction *m_insertAction; +}; + +#endif // PREVIEWSELECTORBAR_H diff --git a/src/webtab/protocolhandler.cpp b/src/webtab/protocolhandler.cpp new file mode 100644 index 00000000..186b93d3 --- /dev/null +++ b/src/webtab/protocolhandler.cpp @@ -0,0 +1,386 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "protocolhandler.h" +#include "protocolhandler.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "historymanager.h" +#include "mainview.h" +#include "mainwindow.h" +#include "newtabpage.h" +#include "urlbar.h" +#include "webpage.h" +#include "webtab.h" + +// KDE Includes +#include <KIO/Job> +#include <KDirLister> +#include <KLocale> +#include <KLocalizedString> +#include <KMessageBox> +#include <KProcess> +#include <KStandardDirs> +#include <KToolInvocation> +#include <KProtocolInfo> +#include <KRun> + +// Qt Includes +#include <QNetworkRequest> +#include <QWebFrame> +#include <QTextDocument> + + +static bool fileItemListLessThan(const KFileItem &s1, const KFileItem &s2) +{ + return s1.name().toLower() < s2.name().toLower(); +} + + +static KFileItemList sortFileList(const KFileItemList &list) +{ + KFileItemList orderedList, dirList, fileList; + + // order dirs before files.. + Q_FOREACH(const KFileItem & item, list) + { + if (item.isDir()) + dirList << item; + else + fileList << item; + } + qStableSort(dirList.begin(), dirList.end(), fileItemListLessThan); + qStableSort(fileList.begin(), fileList.end(), fileItemListLessThan); + + orderedList << dirList; + orderedList << fileList; + + return orderedList; +} + + +// ------------------------------------------------------------------------------------------- + + +ProtocolHandler::ProtocolHandler(QObject *parent) + : QObject(parent) + , _lister(new KDirLister(this)) + , _frame(0) +{ + _lister->setMainWindow(rApp->mainWindow()); +} + + +bool ProtocolHandler::preHandling(const QNetworkRequest &request, QWebFrame *frame) +{ + _url = request.url(); + _frame = frame; + + // javascript handling + if (_url.protocol() == QL1S("javascript")) + { + QString scriptSource = _url.authority(); + if (scriptSource.isEmpty()) + { + // if javascript:<code here> then authority() returns + // an empty string. Extract the source manually + // Use the prettyUrl() since that is unencoded + + // 11 is length of 'javascript:' + // fromPercentEncoding() is used to decode all the % encoded + // characters to normal, so that it is treated as valid javascript + scriptSource = QUrl::fromPercentEncoding(_url.url().mid(11).toAscii()); + if (scriptSource.isEmpty()) + return false; + } + + QVariant result = frame->evaluateJavaScript(scriptSource); + return true; + } + + // "about" handling + if (_url.protocol() == QL1S("about")) + { + QByteArray encodedUrl = _url.toEncoded(); + // let webkit manage the about:blank url... + if (encodedUrl.startsWith(QByteArray("about:blank"))) + { + return false; + } + + if (encodedUrl == QByteArray("about:home")) + { + switch (ReKonfig::newTabStartPage()) + { + case 0: // favorites + _url = KUrl("about:favorites"); + break; + case 1: // closed tabs + _url = KUrl("about:closedTabs"); + break; + case 2: // bookmarks + _url = KUrl("about:bookmarks"); + break; + case 3: // history + _url = KUrl("about:history"); + break; + case 4: // downloads + _url = KUrl("about:downloads"); + break; + case 5: // tabs + _url = KUrl("about:tabs"); + default: // unuseful + break; + } + } + + WebPage *page = qobject_cast<WebPage *>(frame->page()); + page->setIsOnRekonqPage(true); + + NewTabPage p(frame); + p.generate(_url); + + return true; + } + + // "mailto" handling: It needs to be handled both in preHandling (mail url launched) + // and in postHandling (mail links clicked) + if (_url.protocol() == QL1S("mailto")) + { + KToolInvocation::invokeMailer(_url); + return true; + } + + // "apt" handling + // NOTE: this is a stupid workaround to ensure apt protocol works + if (_url.protocol() == QL1S("apt")) + { + kDebug() << "APT URL: " << _url; + (void)new KRun(_url, rApp->mainWindow(), 0, _url.isLocalFile()); + return true; + } + + // let webkit try to load a known (or missing) protocol... + if (KProtocolInfo::isKnownProtocol(_url)) + return false; + + // Error Message, for those protocols we cannot handle + KMessageBox::error(rApp->mainWindow(), i18nc("@info", "rekonq does not know how to handle this protocol: %1", _url.protocol())); + + return true; +} + + +bool ProtocolHandler::postHandling(const QNetworkRequest &request, QWebFrame *frame) +{ + _url = request.url(); + _frame = frame; + + // "http(s)" (fast) handling + if (_url.protocol() == QL1S("http") || _url.protocol() == QL1S("https")) + return false; + + // "mailto" handling: It needs to be handled both here(mail links clicked) + // and in prehandling (mail url launched) + if (_url.protocol() == QL1S("mailto")) + { + KToolInvocation::invokeMailer(_url); + return true; + } + + // "ftp" handling. A little bit "hard" handling this. Hope I found + // the best solution. + // My idea is: webkit cannot handle in any way ftp. So we have surely to return true here. + // We start trying to guess what the url represent: it's a dir? show its contents (and download them). + // it's a file? download it. It's another thing? beat me, but I don't know what to do... + if (_url.protocol() == QL1S("ftp")) + { + KIO::StatJob *job = KIO::stat(_url); + connect(job, SIGNAL(result(KJob*)), this, SLOT(slotMostLocalUrlResult(KJob*))); + return true; + } + + // "file" handling. This is quite trivial :) + if (_url.protocol() == QL1S("file")) + { + QFileInfo fileInfo(_url.path()); + if (fileInfo.isDir()) + { + connect(_lister, SIGNAL(newItems(KFileItemList)), this, SLOT(showResults(KFileItemList))); + _lister->openUrl(_url); + + return true; + } + + return false; + } + + // we cannot handle this protocol in any way. + // Try KRunning it... + if (KProtocolInfo::isKnownProtocol(_url)) + { + (void)new KRun(_url, rApp->mainWindow(), 0, _url.isLocalFile()); + return true; + } + + return false; +} + + +// --------------------------------------------------------------------------------------------------------------------------- + + +void ProtocolHandler::showResults(const KFileItemList &list) +{ + if (!_lister->rootItem().isNull() && _lister->rootItem().isReadable() && _lister->rootItem().isFile()) + { + emit downloadUrl(_lister->rootItem().url()); + } + else + { + QString html = dirHandling(list); + _frame->setHtml(html); + qobject_cast<WebPage *>(_frame->page())->setIsOnRekonqPage(true); + + rApp->mainWindow()->mainView()->currentUrlBar()->setQUrl(_url); + rApp->mainWindow()->currentTab()->setFocus(); + rApp->historyManager()->addHistoryEntry(_url, _url.prettyUrl()); + } +} + + +QString ProtocolHandler::dirHandling(const KFileItemList &list) +{ + if (!_lister) + { + return QString("rekonq error, sorry :("); + } + + // let me modify it.. + KUrl rootUrl = _url; + + // display "rekonq info" page + QString infoFilePath = KStandardDirs::locate("data", "rekonq/htmls/rekonqinfo.html"); + QFile file(infoFilePath); + + bool isOpened = file.open(QIODevice::ReadOnly); + if (!isOpened) + { + return QString("rekonq error, sorry :("); + } + + // 1. default data path + QString dataPath = QL1S("file://") + infoFilePath; + dataPath.remove(QL1S("/htmls/rekonqinfo.html")); + + // 2. title + QString title = _url.prettyUrl(); + + // 3. main content + QString msg = i18nc("%1=an URL", "<h2>Index of %1</h2>", _url.prettyUrl()); + + + if (rootUrl.cd("..")) + { + QString path = rootUrl.prettyUrl(); + QString uparrow = KIconLoader::global()->iconPath("arrow-up", KIconLoader::Small); + msg += "<img src=\"file://" + uparrow + "\" alt=\"up-arrow\" />"; + msg += "<a href=\"" + path + "\">" + i18n("Up to higher level directory") + "</a><br /><br />"; + } + + msg += QL1S("<table width=\"95%\" align=\"center\">"); + msg += QL1S("<tr>"); + msg += QL1S("<th align=\"left\">") + i18n("Name") + QL1S("</th>"); + msg += QL1S("<th align=\"center\">") + i18n("Size") + QL1S("</th>"); + msg += QL1S("<th align=\"right\">") + i18n("Last Modified") + QL1S("</th>"); + msg += QL1S("</tr>"); + + KFileItemList orderedList = sortFileList(list); + Q_FOREACH(const KFileItem & item, orderedList) + { + msg += QL1S("<tr>"); + QString fullPath = Qt::escape(item.url().prettyUrl()); + + QString iconName = item.iconName(); + QString icon = QString("file://") + KIconLoader::global()->iconPath(iconName, KIconLoader::Small); + + msg += QL1S("<td width=\"70%\">"); + msg += QL1S("<img src=\"") + icon + QL1S("\" alt=\"") + iconName + QL1S("\" /> "); + msg += QL1S("<a href=\"") + fullPath + QL1S("\">") + Qt::escape(item.name()) + QL1S("</a>"); + msg += QL1S("</td>"); + + msg += QL1S("<td align=\"right\">"); + if (item.isFile()) + { + msg += KGlobal::locale()->formatByteSize(item.size(), 1); + } + msg += QL1S("</td>"); + + msg += QL1S("<td align=\"right\">"); + msg += item.timeString(); + msg += QL1S("</td>"); + + msg += QL1S("</tr>"); + } + msg += QL1S("</table>"); + + // done. Replace variables and show it + QString html = QL1S(file.readAll()); + + html.replace(QL1S("$DEFAULT_PATH"), dataPath); + html.replace(QL1S("$PAGE_TITLE"), title); + html.replace(QL1S("$MAIN_CONTENT"), msg); + + return html; +} + + +void ProtocolHandler::slotMostLocalUrlResult(KJob *job) +{ + if (job->error()) + { + kDebug() << "JOB ERROR: " << job->errorString(); + // TODO + } + else + { + KIO::StatJob *statJob = static_cast<KIO::StatJob*>(job); + KIO::UDSEntry entry = statJob->statResult(); + if (entry.isDir()) + { + connect(_lister, SIGNAL(newItems(KFileItemList)), this, SLOT(showResults(KFileItemList))); + _lister->openUrl(_url); + } + else + { + emit downloadUrl(_url); + } + } +} diff --git a/src/webtab/protocolhandler.h b/src/webtab/protocolhandler.h new file mode 100644 index 00000000..f35fdcc6 --- /dev/null +++ b/src/webtab/protocolhandler.h @@ -0,0 +1,83 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + + +#ifndef PROTOCOL_HANDLER_H +#define PROTOCOL_HANDLER_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KUrl> + +// Qt Includes +#include <QObject> + +// Forward Declarations +class KDirLister; +class KFileItemList; +class KJob; + +class QNetworkRequest; +class QWebFrame; + + +class REKONQ_TESTS_EXPORT ProtocolHandler : public QObject +{ + Q_OBJECT + +public: + ProtocolHandler(QObject *parent = 0); + + /** + * This function handles all the protocols that have to be handled before + * WebKit does + */ + bool preHandling(const QNetworkRequest &request, QWebFrame *frame); + + /** + * This function handles all the protocols that have to be handled after + * WebKit tried to + */ + bool postHandling(const QNetworkRequest &request, QWebFrame *frame); + +Q_SIGNALS: + void downloadUrl(const KUrl &); + +private Q_SLOTS: + void showResults(const KFileItemList &); + void slotMostLocalUrlResult(KJob *); + +private: + QString dirHandling(const KFileItemList &list); + + KDirLister *_lister; + QWebFrame *_frame; + KUrl _url; +}; + +#endif // PROTOCOL_HANDLER_H diff --git a/src/webtab/searchenginebar.cpp b/src/webtab/searchenginebar.cpp new file mode 100644 index 00000000..441facf7 --- /dev/null +++ b/src/webtab/searchenginebar.cpp @@ -0,0 +1,93 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "searchenginebar.h" +#include "searchenginebar.moc" + +// Auto Includes +#include "rekonq.h" + +// KDE Includes +#include <KIcon> +#include <KIconLoader> +#include <KAction> +#include <KLocalizedString> + +// Qt Includes +#include <QProcess> + + +SearchEngineBar::SearchEngineBar(QWidget *parent) + : KMessageWidget(parent) +{ + connect(this, SIGNAL(accepted()), this, SLOT(hideAndDelete())); + connect(this, SIGNAL(accepted()), this, SLOT(slotAccepted())); + + connect(this, SIGNAL(rejected()), this, SLOT(hideAndDelete())); + connect(this, SIGNAL(rejected()), this, SLOT(slotRejected())); + + setMessageType(KMessageWidget::Information); + + QSize sz = size(); + sz.setWidth(qobject_cast<QWidget *>(parent)->size().width()); + resize(sz); + + setCloseButtonVisible(false); + + setText(i18n("You don't have a default search engine set. Without it, rekonq will not show proper url suggestions.")); + + KAction *acceptAction = new KAction(i18n("Set it"), this); + connect(acceptAction, SIGNAL(triggered(bool)), this, SIGNAL(accepted())); + addAction(acceptAction); + + KAction *rejectAction = new KAction(i18n("Ignore"), this); + connect(rejectAction, SIGNAL(triggered(bool)), this, SIGNAL(rejected())); + addAction(rejectAction); +} + + +void SearchEngineBar::hideAndDelete() +{ + animatedHide(); + deleteLater(); +} + + +void SearchEngineBar::slotAccepted() +{ + QProcess *proc = new QProcess(parent()); + QStringList args; + args << QL1S("ebrowsing"); + proc->start(QL1S("kcmshell4"), args); +} + + +void SearchEngineBar::slotRejected() +{ + // Remember users choice + ReKonfig::setCheckDefaultSearchEngine(false); +} diff --git a/src/webtab/searchenginebar.h b/src/webtab/searchenginebar.h new file mode 100644 index 00000000..7ee1f90c --- /dev/null +++ b/src/webtab/searchenginebar.h @@ -0,0 +1,55 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef SEARCH_ENGINE_BAR_H +#define SEARCH_ENGINE_BAR_H + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KMessageWidget> + + +class REKONQ_TESTS_EXPORT SearchEngineBar : public KMessageWidget +{ + Q_OBJECT + +public: + explicit SearchEngineBar(QWidget *parent); + +private Q_SLOTS: + void hideAndDelete(); + void slotAccepted(); + void slotRejected(); + +Q_SIGNALS: + void accepted(); + void rejected(); +}; + + +#endif // SEARCH_ENGINE_BAR_H diff --git a/src/webtab/sslinfo.ui b/src/webtab/sslinfo.ui new file mode 100644 index 00000000..c1c67b47 --- /dev/null +++ b/src/webtab/sslinfo.ui @@ -0,0 +1,392 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SslInfo</class> + <widget class="QWidget" name="SslInfo"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>539</width> + <height>459</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string><h2>Certificate Information</h2></string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_6"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Certificate Chain:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="certInfoLabel"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_5"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_20"> + <property name="text"> + <string><h4>Issued To:</h4></string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_6"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Common Name (CN):</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="subjectCN"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Organization (O):</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="subjectO"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Organizational Unit (OU):</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="subjectOU"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Serial Number:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="subjectSN"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_21"> + <property name="text"> + <string><h4>Issued By:</h4></string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label_10"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Common Name (CN):</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="issuerCN"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_12"> + <property name="text"> + <string>Organization (O):</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>Organizational Unit (OU):</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="issuerOU"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="issuerO"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_22"> + <property name="text"> + <string><h4>Validity Period:</h4></string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="0" column="0"> + <widget class="QLabel" name="label_14"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Issued on:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="issuedOn"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_18"> + <property name="text"> + <string>Expires on:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="expiresOn"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_23"> + <property name="text"> + <string><h4>Digests:</h4></string> + </property> + </widget> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QLabel" name="label_36"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>160</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>Md5:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="md5"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_38"> + <property name="text"> + <string>SHA-1:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="sha1"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/webtab/sslinfodialog.cpp b/src/webtab/sslinfodialog.cpp new file mode 100644 index 00000000..2f0b44e4 --- /dev/null +++ b/src/webtab/sslinfodialog.cpp @@ -0,0 +1,176 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "sslinfodialog.h" +#include "sslinfodialog.moc" + +// KDE Includes +#include <KFileDialog> + +#include <kglobal.h> +#include <klocale.h> +#include <ktcpsocket.h> + +// Qt Includes +#include <QDate> +#include <QFile> + +#include <QLabel> +#include <QTextDocument> + +#include <QSslCertificate> + + +SslInfoDialog::SslInfoDialog(const QString &host, const WebSslInfo &info, QWidget *parent) + : KDialog(parent) + , m_host(host) + , m_info(info) +{ + setCaption(i18n("Rekonq SSL Information")); + setAttribute(Qt::WA_DeleteOnClose); + + setMinimumWidth(300); + + setButtons(KDialog::User1 | KDialog::Close); + + setButtonGuiItem(User1, KGuiItem(i18n("Export"), QL1S("view-certificate-export"))); + connect(this, SIGNAL(user1Clicked()), this, SLOT(exportCert())); + + ui.setupUi(mainWidget()); + + // ------------------------------------------------ + QList<QSslCertificate> caList = m_info.certificateChain(); + + Q_FOREACH(const QSslCertificate & cert, caList) + { + QString name = cert.subjectInfo(QSslCertificate::CommonName); + if (name.isEmpty()) + name = cert.subjectInfo(QSslCertificate::Organization); + if (name.isEmpty()) + name = cert.serialNumber(); + ui.comboBox->addItem(name); + } + connect(ui.comboBox, SIGNAL(activated(int)), this, SLOT(displayFromChain(int))); + + displayFromChain(0); +} + + +void SslInfoDialog::showCertificateInfo(QSslCertificate subjectCert, const QStringList &certErrors) +{ + QStringList sl = certErrors; + QString c = sl.takeFirst(); + c += QL1S("<ul>"); + Q_FOREACH(const QString & s, sl) + { + c += QL1S("<li>") + s + QL1S("</li>"); + } + c += QL1S("</ul>"); + ui.certInfoLabel->setText(c); + + ui.subjectCN->setText(Qt::escape(subjectCert.subjectInfo(QSslCertificate::CommonName))); + ui.subjectO->setText(Qt::escape(subjectCert.subjectInfo(QSslCertificate::Organization))); + ui.subjectOU->setText(Qt::escape(subjectCert.subjectInfo(QSslCertificate::OrganizationalUnitName))); + ui.subjectSN->setText(Qt::escape(subjectCert.serialNumber())); + + ui.issuerCN->setText(Qt::escape(subjectCert.issuerInfo(QSslCertificate::CommonName))); + ui.issuerO->setText(Qt::escape(subjectCert.issuerInfo(QSslCertificate::Organization))); + ui.issuerOU->setText(Qt::escape(subjectCert.issuerInfo(QSslCertificate::OrganizationalUnitName))); + + ui.issuedOn->setText(Qt::escape(subjectCert.effectiveDate().date().toString(Qt::SystemLocaleShortDate))); + ui.expiresOn->setText(Qt::escape(subjectCert.expiryDate().date().toString(Qt::SystemLocaleShortDate))); + + ui.md5->setText(Qt::escape(subjectCert.digest(QCryptographicHash::Md5).toHex())); + ui.sha1->setText(Qt::escape(subjectCert.digest(QCryptographicHash::Sha1).toHex())); +} + + +void SslInfoDialog::displayFromChain(int i) +{ + QList<QSslCertificate> caList = m_info.certificateChain(); + QSslCertificate cert = caList.at(i); + + QStringList errors = SslInfoDialog::errorsFromString(m_info.certificateErrors()).at(i); + + if (cert.isValid() && errors.isEmpty()) + { + QStringList certInfo; + certInfo << i18n("The Certificate is Valid!"); + showCertificateInfo(cert, certInfo); + } + else + { + errors.prepend(i18n("The certificate for this site is NOT valid for the following reasons:")); + showCertificateInfo(cert, errors); + } +} + + +void SslInfoDialog::exportCert() +{ + QSslCertificate cert = m_info.certificateChain().at(ui.comboBox->currentIndex()); + + if (cert.isNull()) + return; + + QString name = m_host + QL1S(".pem"); + + QString certPath = KFileDialog::getSaveFileName(name, QString(), this); + + QFile file(certPath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return; + + QTextStream out(&file); + out << cert.toPem(); +} + + +// static ------------------------------------------------------------------------------------------- +QList<QStringList> SslInfoDialog::errorsFromString(const QString &s) +{ + QList<QStringList> resultList; + + QStringList sl1 = s.split('\n', QString::KeepEmptyParts); + + Q_FOREACH(const QString & certErrors, sl1) + { + QStringList errors; + QStringList sl = certErrors.split('\t', QString::SkipEmptyParts); + Q_FOREACH(const QString & s, sl) + { + bool didConvert; + KSslError::Error error = static_cast<KSslError::Error>(s.trimmed().toInt(&didConvert)); + if (didConvert) + { + errors << KSslError(error).errorString(); + } + } + resultList << errors; + } + return resultList; +} diff --git a/src/webtab/sslinfodialog.h b/src/webtab/sslinfodialog.h new file mode 100644 index 00000000..94f610c6 --- /dev/null +++ b/src/webtab/sslinfodialog.h @@ -0,0 +1,79 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef SSL_INFO_DIALOG_H +#define SSL_INFO_DIALOG_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Local Includes +#include "websslinfo.h" + +// Ui Includes +#include "ui_sslinfo.h" + +// KDE Includes +#include <KDialog> + +// Qt Includes +#include <QString> + +// Forward Declarations +class QSslCertificate; + + +/** + * Rekonq SSL Information Dialog + * + * This class creates a dialog that can be used to display information about + * an SSL session. + * + */ +class SslInfoDialog : public KDialog +{ + Q_OBJECT + +public: + explicit SslInfoDialog(const QString &host, const WebSslInfo &info, QWidget *parent = 0); + + static QList<QStringList> errorsFromString(const QString &s); + +private Q_SLOTS: + void displayFromChain(int); + void exportCert(); + +private: + void showCertificateInfo(QSslCertificate, const QStringList &certErrors); + + QString m_host; + WebSslInfo m_info; + + Ui::SslInfo ui; +}; + +#endif // SSL_INFO_DIALOG_H diff --git a/src/webtab/walletbar.cpp b/src/webtab/walletbar.cpp new file mode 100644 index 00000000..01a6d658 --- /dev/null +++ b/src/webtab/walletbar.cpp @@ -0,0 +1,103 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009-2011 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "walletbar.h" +#include "walletbar.moc" + +// Auto Includes +#include "rekonq.h" + +// KDE Includes +#include <KIcon> +#include <KLocalizedString> + +// Qt Includes +#include <QAction> + + +WalletBar::WalletBar(QWidget *parent) + : KMessageWidget(parent) +{ + setMessageType(KMessageWidget::Warning); + + QSize sz = size(); + sz.setWidth(qobject_cast<QWidget *>(parent)->size().width()); + resize(sz); + + setCloseButtonVisible(false); + + QAction *rememberAction = new QAction(KIcon("document-save"), i18n("Remember"), this); + connect(rememberAction, SIGNAL(triggered(bool)), this, SLOT(rememberData())); + addAction(rememberAction); + + QAction *neverHereAction = new QAction(KIcon("process-stop"), i18n("Never for This Site"), this); + connect(neverHereAction, SIGNAL(triggered(bool)), this, SLOT(neverRememberData())); + addAction(neverHereAction); + + QAction *notNowAction = new QAction(KIcon("dialog-cancel"), i18n("Not Now"), this); + connect(notNowAction, SIGNAL(triggered(bool)), this, SLOT(notNowRememberData())); + addAction(notNowAction); +} + + +void WalletBar::rememberData() +{ + emit saveFormDataAccepted(m_key); + + animatedHide(); + deleteLater(); +} + + +void WalletBar::neverRememberData() +{ + // add url to the blacklist + QStringList list = ReKonfig::walletBlackList(); + list << m_url.toString(); + ReKonfig::setWalletBlackList(list); + + notNowRememberData(); +} + + +void WalletBar::notNowRememberData() +{ + emit saveFormDataRejected(m_key); + + animatedHide(); + deleteLater(); +} + + + +void WalletBar::onSaveFormData(const QString &key, const QUrl &url) +{ + setText(i18n("Do you want rekonq to remember the password on %1?", url.host())); + + m_key = key; + m_url = url; +} diff --git a/src/webtab/walletbar.h b/src/webtab/walletbar.h new file mode 100644 index 00000000..f1bbd2cb --- /dev/null +++ b/src/webtab/walletbar.h @@ -0,0 +1,66 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009-2011 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WALLET_BAR_H +#define WALLET_BAR_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KMessageWidget> + +// Qt Includes +#include <QUrl> +#include <QString> + + +class REKONQ_TESTS_EXPORT WalletBar : public KMessageWidget +{ + Q_OBJECT + +public: + WalletBar(QWidget *parent); + +private Q_SLOTS: + void rememberData(); + void neverRememberData(); + void notNowRememberData(); + +public Q_SLOTS: + void onSaveFormData(const QString &, const QUrl &); + +Q_SIGNALS: + void saveFormDataAccepted(const QString &); + void saveFormDataRejected(const QString &); + +private: + QString m_key; + QUrl m_url; +}; + +#endif // WALLET_BAR_H diff --git a/src/webtab/webpage.cpp b/src/webtab/webpage.cpp new file mode 100644 index 00000000..59262862 --- /dev/null +++ b/src/webtab/webpage.cpp @@ -0,0 +1,777 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 Benjamin C. Meyer <ben@meyerhome.net> +* Copyright (C) 2008 Dirk Mueller <mueller@kde.org> +* Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org> +* Copyright (C) 2008 Michael Howell <mhowell123@gmail.com> +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* Copyright (C) 2009-2010 Dawit Alemayehu <adawit at kde dot org> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "webpage.h" +#include "webpage.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "adblockmanager.h" +#include "application.h" +#include "downloadmanager.h" +#include "historymanager.h" +#include "iconmanager.h" +#include "mainview.h" +#include "mainwindow.h" +#include "networkaccessmanager.h" +#include "urlbar.h" +#include "webpluginfactory.h" +#include "websnap.h" +#include "webtab.h" +#include "searchengine.h" +#include "sslwidget.h" +#include "sslinfodialog.h" + +// KDE Includes +#include <KTemporaryFile> +#include <KStandardDirs> +#include <KJobUiDelegate> +#include <KLocalizedString> +#include <KMessageBox> +#include <KMimeTypeTrader> +#include <KService> +#include <KWebWallet> +#include <KProtocolInfo> +#include <KRun> + +#include <KIO/JobUiDelegate> + +#include <kparts/browseropenorsavequestion.h> + +// Qt Includes +#include <QTextDocument> +#include <QFileInfo> +#include <QNetworkReply> + + +// Returns true if the scheme and domain of the two urls match... +static bool domainSchemeMatch(const QUrl& u1, const QUrl& u2) +{ + if (u1.scheme() != u2.scheme()) + return false; + + QStringList u1List = u1.host().split(QL1C('.'), QString::SkipEmptyParts); + QStringList u2List = u2.host().split(QL1C('.'), QString::SkipEmptyParts); + + if (qMin(u1List.count(), u2List.count()) < 2) + return false; // better safe than sorry... + + while (u1List.count() > 2) + u1List.removeFirst(); + + while (u2List.count() > 2) + u2List.removeFirst(); + + return (u1List == u2List); +} + + +static void extractMimeType(const QNetworkReply* reply, QString& mimeType) +{ + mimeType.clear(); + const KIO::MetaData& metaData = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap(); + if (metaData.contains(QL1S("content-type"))) + mimeType = metaData.value(QL1S("content-type")); + + if (!mimeType.isEmpty()) + return; + + if (!reply->hasRawHeader("Content-Type")) + return; + + const QString value(QL1S(reply->rawHeader("Content-Type").simplified().constData())); + const int index = value.indexOf(QL1C(';')); + if (index == -1) + mimeType = value; + else + mimeType = value.left(index); +} + + +// --------------------------------------------------------------------------------- + + +WebPage::WebPage(QWidget *parent) + : KWebPage(parent, KWalletIntegration) + , _networkAnalyzer(false) + , _isOnRekonqPage(false) +{ + // handling unsupported content... + setForwardUnsupportedContent(true); + connect(this, SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(handleUnsupportedContent(QNetworkReply*))); + + // rekonq Network Manager + NetworkAccessManager *manager = new NetworkAccessManager(this); + + // set network reply object to emit readyRead when it receives meta data + manager->setEmitReadyReadOnMetaDataChange(true); + + // disable QtWebKit cache to just use KIO one.. + manager->setCache(0); + + // set cookieJar window.. + if (parent && parent->window()) + manager->setWindow(parent->window()); + + setNetworkAccessManager(manager); + + // activate ssl warnings + setSessionMetaData(QL1S("ssl_activate_warnings"), QL1S("TRUE")); + + // ----- Web Plugin Factory + setPluginFactory(new WebPluginFactory(this)); + + // ----- last stuffs + connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(manageNetworkErrors(QNetworkReply*))); + + connect(this, SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequest(QNetworkRequest))); + connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); + + // protocol handler signals + connect(&_protHandler, SIGNAL(downloadUrl(KUrl)), this, SLOT(downloadUrl(KUrl))); + + connect(rApp->iconManager(), SIGNAL(iconChanged()), mainFrame(), SIGNAL(iconChanged())); +} + + +WebPage::~WebPage() +{ + disconnect(); + + QPixmap preview = WebSnap::renderPagePreview(*this); + QString path = WebSnap::imagePathFromUrl(mainFrame()->url().toString()); + QFile::remove(path); + preview.save(path); +} + + +bool WebPage::hasNetworkAnalyzerEnabled() const +{ + return _networkAnalyzer; +}; + + +void WebPage::enableNetworkAnalyzer(bool b) +{ + _networkAnalyzer = b; +}; + + +bool WebPage::isOnRekonqPage() const +{ + return _isOnRekonqPage; +}; + + +void WebPage::setIsOnRekonqPage(bool b) +{ + _isOnRekonqPage = b; +}; + + +KUrl WebPage::loadingUrl() +{ + return _loadingUrl; +}; + + +QString WebPage::suggestedFileName() +{ + return _suggestedFileName; +}; + + +bool WebPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type) +{ + if (_isOnRekonqPage) + { + WebView *view = qobject_cast<WebView *>(parent()); + WebTab *tab = qobject_cast<WebTab *>(view->parent()); + _isOnRekonqPage = false; + tab->setPart(0, KUrl()); // re-enable the view page + } + + // reset webpage values + _suggestedFileName.clear(); + _loadingUrl = request.url(); + + const bool isMainFrameRequest = (frame == mainFrame()); + + if (frame) + { + if (_protHandler.preHandling(request, frame)) + { + return false; + } + + switch (type) + { + case QWebPage::NavigationTypeLinkClicked: + if (_sslInfo.isValid()) + { + setRequestMetaData("ssl_was_in_use", "TRUE"); + } + break; + + case QWebPage::NavigationTypeFormSubmitted: + break; + + case QWebPage::NavigationTypeFormResubmitted: + if (KMessageBox::warningContinueCancel(view(), + i18n("Are you sure you want to send your data again?"), + i18n("Resend form data") + ) + == KMessageBox::Cancel) + { + return false; + } + break; + + case QWebPage::NavigationTypeReload: + setRequestMetaData(QL1S("cache"), QL1S("reload")); + break; + + case QWebPage::NavigationTypeBackOrForward: + case QWebPage::NavigationTypeOther: + break; + + default: + ASSERT_NOT_REACHED(unknown NavigationType); + break; + } + } + + // Get the SSL information sent, if any... + KIO::AccessManager *manager = qobject_cast<KIO::AccessManager*>(networkAccessManager()); + KIO::MetaData metaData = manager->requestMetaData(); + if (metaData.contains(QL1S("ssl_in_use"))) + { + WebSslInfo info; + info.restoreFrom(metaData.toVariant(), request.url()); + info.setUrl(request.url()); + _sslInfo = info; + } + + if (isMainFrameRequest) + { + setRequestMetaData(QL1S("main_frame_request"), QL1S("TRUE")); + if (_sslInfo.isValid() && !domainSchemeMatch(request.url(), _sslInfo.url())) + { + _sslInfo = WebSslInfo(); + } + } + else + { + setRequestMetaData(QL1S("main_frame_request"), QL1S("FALSE")); + } + + return KWebPage::acceptNavigationRequest(frame, request, type); +} + + +WebPage *WebPage::createWindow(QWebPage::WebWindowType type) +{ + // added to manage web modal dialogs + if (type == QWebPage::WebModalDialog) + kDebug() << "Modal Dialog"; + + WebTab *w = 0; + if (ReKonfig::openLinksInNewWindow()) + { + w = rApp->newMainWindow()->mainView()->currentWebTab(); + } + else + { + w = rApp->mainWindow()->mainView()->newWebTab(!ReKonfig::openNewTabsInBackground()); + } + return w->page(); +} + + +void WebPage::handleUnsupportedContent(QNetworkReply *reply) +{ + Q_ASSERT(reply); + + if (!reply) + return; + + // handle protocols WebKit cannot handle... + if (_protHandler.postHandling(reply->request(), mainFrame())) + { + return; + } + + if (reply->error() != QNetworkReply::NoError) + return; + + KIO::Integration::AccessManager::putReplyOnHold(reply); + + // Get mimeType... + extractMimeType(reply, _mimeType); + + // Convert executable text files to plain text... + if (KParts::BrowserRun::isTextExecutable(_mimeType)) + _mimeType = QL1S("text/plain"); + + // Get suggested file name... + const KIO::MetaData& data = reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap(); + _suggestedFileName = data.value(QL1S("content-disposition-filename")); + + kDebug() << "Detected MimeType = " << _mimeType; + kDebug() << "Suggested File Name = " << _suggestedFileName; + // ------------------------------------------------ + + KService::Ptr appService = KMimeTypeTrader::self()->preferredService(_mimeType); + + KUrl replyUrl = reply->url(); + bool isLocal = replyUrl.isLocalFile(); + + if (appService.isNull()) // no service can handle this. We can just download it.. + { + isLocal + ? KMessageBox::sorry(view(), i18n("No service can handle this file.")) + : downloadUrl(reply->url()); + + return; + } + + // NOTE + // This is needed in case rekonq has been associated with something it cannot + // properly handle (eg: xbel files, see BUG:299056). This way we break an eventual + // "recall" loop. + if (appService->exec().trimmed().startsWith(QL1S("rekonq"))) + { + isLocal + ? KMessageBox::sorry(view(), i18n("rekonq cannot properly handle this, sorry")) + : downloadUrl(reply->url()); + + return; + } + + if (isLocal) + { + // Load outside local files + KRun::run(*appService, replyUrl, 0, false, _suggestedFileName); + } + else + { + KParts::BrowserOpenOrSaveQuestion dlg(rApp->mainWindow(), replyUrl, _mimeType); + + if (!_suggestedFileName.isEmpty()) + dlg.setSuggestedFileName(_suggestedFileName); + + // read askEmbedOrSave preferences. If we don't have to show dialog and rekonq settings are + // to automatically choose download dir, we won't show local dir choose dialog + KConfigGroup cg = KConfigGroup(KSharedConfig::openConfig("filetypesrc", KConfig::NoGlobals), QL1S("Notification Messages")); + bool hideDialog = cg.readEntry(QL1S("askEmbedOrSave") + _mimeType, false); + + kDebug() << "Hide dialog for " << _mimeType << "? " << hideDialog; + + switch (dlg.askEmbedOrSave()) + { + case KParts::BrowserOpenOrSaveQuestion::Save: + rApp->downloadManager()->downloadResource(reply->url(), KIO::MetaData(), view(), !hideDialog, _suggestedFileName); + return; + + case KParts::BrowserOpenOrSaveQuestion::Cancel: + return; + + default: // Can happen when "Open with.." is set and "don't ask again" is checked + break; + } + } + + // Handle Post operations that return content... + if (reply->operation() == QNetworkAccessManager::PostOperation) + { + kDebug() << "POST OPERATION: downloading file..."; + QFileInfo finfo(_suggestedFileName.isEmpty() ? _loadingUrl.fileName() : _suggestedFileName); + KTemporaryFile tempFile; + tempFile.setSuffix(QL1C('.') + finfo.suffix()); + tempFile.setAutoRemove(false); + tempFile.open(); + KUrl destUrl; + destUrl.setPath(tempFile.fileName()); + KIO::Job *job = KIO::file_copy(_loadingUrl, destUrl, 0600, KIO::Overwrite); + job->ui()->setWindow(rApp->mainWindow()); + connect(job, SIGNAL(result(KJob*)), this, SLOT(copyToTempFileResult(KJob*))); + return; + } + + // case KParts::BrowserRun::Embed + KParts::ReadOnlyPart *pa = KMimeTypeTrader::createPartInstanceFromQuery<KParts::ReadOnlyPart>(_mimeType, view(), this, QString()); + if (pa) + { + _isOnRekonqPage = true; + + WebView *view = qobject_cast<WebView *>(parent()); + WebTab *tab = qobject_cast<WebTab *>(view->parent()); + tab->setPart(pa, replyUrl); + + UrlBar *bar = tab->urlBar(); + bar->setQUrl(replyUrl); + + rApp->mainWindow()->updateHistoryActions(); + } + else + { + // No parts, just app services. Load it! + // If the app is a KDE one, publish the slave on hold to let it use it. + // Otherwise, run the app and remove it (the io slave...) + KRun::run(*appService, replyUrl, 0, false, _suggestedFileName); + } + + return; +} + + +void WebPage::loadStarted() +{ + _hasAdBlockedElements = false; + rApp->adblockManager()->clearElementsLists(); + + // set zoom factor + QString val; + KSharedConfig::Ptr config = KGlobal::config(); + KConfigGroup group(config, "Zoom"); + val = group.readEntry(_loadingUrl.host(), QString("10")); + + int value = val.toInt(); + if (value != 10) + mainFrame()->setZoomFactor(QVariant(value).toReal() / 10); // Don't allox max +1 values +} + + +void WebPage::loadFinished(bool ok) +{ + Q_UNUSED(ok); + + // Provide site icon. Can this be moved to loadStarted?? + rApp->iconManager()->provideIcon(mainFrame(), _loadingUrl); + + // Apply adblock manager hiding rules + rApp->adblockManager()->applyHidingRules(this); + + // KWallet Integration + QStringList list = ReKonfig::walletBlackList(); + if (wallet() + && !list.contains(mainFrame()->url().toString()) + ) + { + wallet()->fillFormData(mainFrame()); + } +} + + +void WebPage::manageNetworkErrors(QNetworkReply *reply) +{ + Q_ASSERT(reply); + + QWebFrame* frame = qobject_cast<QWebFrame *>(reply->request().originatingObject()); + if (!frame) + return; + + const bool isMainFrameRequest = (frame == mainFrame()); + + // Only deal with non-redirect responses... + const QVariant redirectVar = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirectVar.isValid()) + { + _sslInfo.restoreFrom(reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)), reply->url()); + return; + } + + // We are just managing loading URLs errors + if (reply->request().url() != _loadingUrl) + return; + + // NOTE: These are not all networkreply errors, + // but just that supported directly by KIO + switch (reply->error()) + { + + case QNetworkReply::NoError: // no error. Simple :) + if (isMainFrameRequest) + { + // Obtain and set the SSL information if any... + _sslInfo.restoreFrom(reply->attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)), reply->url()); + _sslInfo.setUrl(reply->url()); + } + break; + + case QNetworkReply::OperationCanceledError: // operation canceled via abort() or close() calls + // ignore this.. + return; + + // WARNING: This is also typical adblocked element error: IGNORE THIS! + case QNetworkReply::ContentAccessDenied: // access to remote content denied + break; + + case QNetworkReply::UnknownNetworkError: // unknown network-related error detected + // last chance for the strange things (eg: FTP, custom schemes, etc...) + if (_protHandler.postHandling(reply->request(), mainFrame())) + return; + + case QNetworkReply::ConnectionRefusedError: // remote server refused connection + case QNetworkReply::HostNotFoundError: // invalid hostname + case QNetworkReply::TimeoutError: // connection time out + case QNetworkReply::ProxyNotFoundError: // invalid proxy hostname + case QNetworkReply::ContentOperationNotPermittedError: // operation requested on remote content not permitted + case QNetworkReply::ContentNotFoundError: // remote content not found on server (similar to HTTP error 404) + case QNetworkReply::ProtocolUnknownError: // Unknown protocol + case QNetworkReply::ProtocolInvalidOperationError: // requested operation is invalid for this protocol + default: + kDebug() << "ERROR " << reply->error() << ": " << reply->errorString(); + if (reply->url() == _loadingUrl) + { + frame->setHtml(errorPage(reply)); + if (isMainFrameRequest) + { + _isOnRekonqPage = true; + + WebView *view = qobject_cast<WebView *>(parent()); + WebTab *tab = qobject_cast<WebTab *>(view->parent()); + UrlBar *bar = tab->urlBar(); + bar->setQUrl(_loadingUrl); + + rApp->mainWindow()->updateHistoryActions(); + } + } + break; + + } +} + + +QString WebPage::errorPage(QNetworkReply *reply) +{ + // display "not found" page + QString notfoundFilePath = KStandardDirs::locate("data", "rekonq/htmls/rekonqinfo.html"); + QFile file(notfoundFilePath); + + bool isOpened = file.open(QIODevice::ReadOnly); + if (!isOpened) + { + return QString("Couldn't open the rekonqinfo.html file"); + } + + // NOTE: + // this, to take care about XSS (see BUG 217464)... + QString urlString = Qt::escape(reply->url().toString()); + + // 1. data path + QString dataPath = QL1S("file://") + notfoundFilePath; + dataPath.remove(QL1S("/htmls/rekonqinfo.html")); + + // 2. title + QString title = i18n("There was a problem while loading the page"); + + // 3. main content + QString msg; + + msg += i18n("<h1>Oops! Rekonq cannot load <em>%1</em></h1>", urlString); + + + msg += i18n("<h2>Wrongly typed?</h2>"); + + msg += QL1S("<table>"); + msg += QL1S("<tr><td>"); + + msg += QL1S("<p>"); + + msg += i18n("We tried to load url: %1.<br />", urlString); + msg += i18n("Check your address for errors like <em>ww.kde.org</em> instead of <em>www.kde.org</em>.<br />"); + msg += i18n("If you spelled right, just try to <a href=\"%1\">reload it</a>.<br />", urlString); + msg += i18n("Otherwise, just be careful the next time around."); + + msg += QL1S("</p>"); + + QString laughIconPath = QString("file://") + KIconLoader::global()->iconPath("face-laugh" , -KIconLoader::SizeHuge, false); + msg += QL1S("</td><td>"); + msg += QL1S("<img src=\"") + laughIconPath + QL1S("\" />"); + msg += QL1S("</td></tr></table>"); + + + msg += i18n("<h2>Network problems?</h2>"); + + QString faceIconPath = QString("file://") + KIconLoader::global()->iconPath("face-surprise" , -KIconLoader::SizeHuge, false); + msg += QL1S("<table>"); + msg += QL1S("<tr><td width=\"100px\">"); + msg += QL1S("<img src=\"") + faceIconPath + QL1S("\" />"); + msg += QL1S("</td><td>"); + + msg += QL1S("<p>"); + + msg += i18n("Maybe you are having problems with your network.<br />"); + msg += i18n("Try checking your <a href=\"%1\">network connections</a>", QL1S("about:settings/network")); + msg += i18n(", your <a href=\"%1\">proxy settings</a> ", QL1S("about:settings/proxy")); + msg += i18n("and your <a href=\"%1\">firewall</a>.<br />", QL1S("about:settings/firewall")); + msg += i18n("Then try again.<br />"); + + msg += QL1S("</p>"); + + msg += QL1S("</td></tr></table>"); + + + msg += i18n("<h2>Suggestions</h2>"); + + msg += QL1S("<table>"); + msg += QL1S("<tr><td>"); + + msg += QL1S("<p>"); + + // Default SearchEngine + KService::Ptr defaultEngine = SearchEngine::defaultEngine(); + + if (defaultEngine) + { + msg += i18n("Consult your default search engine about:"); + msg += QL1S(" <a href=\"") + SearchEngine::buildQuery(defaultEngine, urlString) + QL1S("\">"); + msg += i18n("search with %1", defaultEngine->name()); + msg += QL1S("</a>!<br />"); + } + else + { + msg += i18n("You don't have a default search engine set. We won't suggest you one."); + } + + msg += i18n("At least, you can consult a cached snapshot of the site: <br />"); + msg += i18n("Try checking the <a href=\"%1\">Wayback Machine</a>", QL1S("http://wayback.archive.org/web/*/") + urlString); + msg += i18n(" or the <a href=\"%1\">Google Cache</a>.", QL1S("http://google.com/search?q=cache:") + urlString); + + msg += QL1S("</p>"); + + QString winkIconPath = QString("file://") + KIconLoader::global()->iconPath("face-wink" , -KIconLoader::SizeHuge, false); + msg += QL1S("</td><td>"); + msg += QL1S("<img src=\"") + winkIconPath + QL1S("\" />"); + msg += QL1S("</td></tr></table>"); + + // done. Replace variables and show it + QString html = QL1S(file.readAll()); + + html.replace(QL1S("$DEFAULT_PATH"), dataPath); + html.replace(QL1S("$PAGE_TITLE"), title); + html.replace(QL1S("$MAIN_CONTENT"), msg); + + return html; +} + + +void WebPage::downloadRequest(const QNetworkRequest &request) +{ + rApp->downloadManager()->downloadResource(request.url(), + request.attribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData)).toMap(), + view()); +} + + +void WebPage::downloadUrl(const KUrl &url) +{ + rApp->downloadManager()->downloadResource(url, KIO::MetaData(), view()); +} + + +void WebPage::downloadAllContentsWithKGet() +{ + QSet<QString> contents; + KUrl baseUrl(currentFrame()->url()); + KUrl relativeUrl; + + QWebElementCollection images = mainFrame()->documentElement().findAll("img"); + Q_FOREACH(const QWebElement & img, images) + { + relativeUrl.setEncodedUrl(img.attribute("src").toUtf8(), KUrl::TolerantMode); + contents << baseUrl.resolved(relativeUrl).toString(); + } + + QWebElementCollection links = mainFrame()->documentElement().findAll("a"); + Q_FOREACH(const QWebElement & link, links) + { + relativeUrl.setEncodedUrl(link.attribute("href").toUtf8(), KUrl::TolerantMode); + contents << baseUrl.resolved(relativeUrl).toString(); + } + + rApp->downloadManager()->downloadLinksWithKGet(QVariant(contents.toList())); +} + + +void WebPage::showSSLInfo(QPoint pos) +{ + if (mainFrame()->url().scheme() == QL1S("https")) + { + SSLWidget *widget = new SSLWidget(mainFrame()->url(), _sslInfo, view()); + widget->showAt(pos); + } + else + { + KMessageBox::information(view(), + i18n("This site does not contain SSL information."), + i18nc("Secure Sockets Layer", "SSL") + ); + } +} + + +void WebPage::copyToTempFileResult(KJob* job) +{ + if (job->error()) + job->uiDelegate()->showErrorMessage(); + else + (void)KRun::runUrl(static_cast<KIO::FileCopyJob *>(job)->destUrl(), _mimeType, rApp->mainWindow()); +} + + +bool WebPage::hasSslValid() const +{ + QList<QSslCertificate> certList = _sslInfo.certificateChain(); + + if (certList.isEmpty()) + return false; + + const QSslCertificate cert = certList.at(0); + if (!cert.isValid()) + return false; + + QList<QStringList> errorList = SslInfoDialog::errorsFromString(_sslInfo.certificateErrors()); + if (!errorList.isEmpty()) + { + QStringList list = errorList.at(0); + if (!list.isEmpty()) + return false; + } + + return true; +} diff --git a/src/webtab/webpage.h b/src/webtab/webpage.h new file mode 100644 index 00000000..abc9f64c --- /dev/null +++ b/src/webtab/webpage.h @@ -0,0 +1,114 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008 Benjamin C. Meyer <ben@meyerhome.net> +* Copyright (C) 2008 Dirk Mueller <mueller@kde.org> +* Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org> +* Copyright (C) 2008 Michael Howell <mhowell123@gmail.com> +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WEBPAGE_H +#define WEBPAGE_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Local Includes +#include "protocolhandler.h" +#include "websslinfo.h" + +// KDE Includes +#include <KWebPage> + + +class REKONQ_TESTS_EXPORT WebPage : public KWebPage +{ + Q_OBJECT + +public: + explicit WebPage(QWidget *parent = 0); + ~WebPage(); + + bool hasNetworkAnalyzerEnabled() const; + void enableNetworkAnalyzer(bool b); + + bool isOnRekonqPage() const; + void setIsOnRekonqPage(bool b); + + KUrl loadingUrl(); + + QString suggestedFileName(); + + inline bool hasAdBlockedElements() const + { + return _hasAdBlockedElements; + }; + + inline void setHasAdBlockedElements(bool b) + { + _hasAdBlockedElements = b; + }; + + bool hasSslValid() const; + +public Q_SLOTS: + void downloadAllContentsWithKGet(); + + virtual void downloadRequest(const QNetworkRequest &request); + virtual void downloadUrl(const KUrl &url); + +protected: + WebPage *createWindow(WebWindowType type); + + virtual bool acceptNavigationRequest(QWebFrame *frame, + const QNetworkRequest &request, + NavigationType type); + +private Q_SLOTS: + void handleUnsupportedContent(QNetworkReply *reply); + void manageNetworkErrors(QNetworkReply *reply); + void loadStarted(); + void loadFinished(bool); + void showSSLInfo(QPoint); + + void copyToTempFileResult(KJob*); + +private: + QString errorPage(QNetworkReply *reply); + KUrl _loadingUrl; + + ProtocolHandler _protHandler; + WebSslInfo _sslInfo; + + QString _mimeType; + QString _suggestedFileName; + + bool _networkAnalyzer; + bool _isOnRekonqPage; + bool _hasAdBlockedElements; +}; + +#endif diff --git a/src/webtab/webpluginfactory.cpp b/src/webtab/webpluginfactory.cpp new file mode 100644 index 00000000..b62af1c8 --- /dev/null +++ b/src/webtab/webpluginfactory.cpp @@ -0,0 +1,93 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "webpluginfactory.h" +#include "webpluginfactory.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "clicktoflash.h" + + +WebPluginFactory::WebPluginFactory(QObject *parent) + : KWebPluginFactory(parent) + , _loadClickToFlash(false) +{ + connect(this, SIGNAL(signalLoadClickToFlash(bool)), SLOT(setLoadClickToFlash(bool))); +} + + +void WebPluginFactory::setLoadClickToFlash(bool load) +{ + _loadClickToFlash = load; +} + + +QObject *WebPluginFactory::create(const QString &mimeType, + const QUrl &url, + const QStringList &argumentNames, + const QStringList &argumentValues) const +{ + kDebug() << "loading mimeType: " << mimeType; + + switch (ReKonfig::pluginsEnabled()) + { + case 0: + kDebug() << "No plugins found for" << mimeType << ". Falling back to KDEWebKit ones..."; + return KWebPluginFactory::create(mimeType, url, argumentNames, argumentValues); + + case 1: + if (mimeType != QString("application/x-shockwave-flash") + && mimeType != QString("application/futuresplash")) + break; + + if (_loadClickToFlash) + { + emit signalLoadClickToFlash(false); + return KWebPluginFactory::create(mimeType, url, argumentNames, argumentValues); + } + else + { + ClickToFlash* ctf = new ClickToFlash(url); + connect(ctf, SIGNAL(signalLoadClickToFlash(bool)), this, SLOT(setLoadClickToFlash(bool))); + return ctf; + } + break; + + case 2: + return 0; + + default: + ASSERT_NOT_REACHED("oh oh.. this should NEVER happen.."); + break; + } + + return KWebPluginFactory::create(mimeType, url, argumentNames, argumentValues); +} diff --git a/src/webtab/webpluginfactory.h b/src/webtab/webpluginfactory.h new file mode 100644 index 00000000..b1bcd967 --- /dev/null +++ b/src/webtab/webpluginfactory.h @@ -0,0 +1,65 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2010 by Matthieu Gicquel <matgic78 at gmail dot com> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WEB_PLUGIN_FACTORY_H +#define WEB_PLUGIN_FACTORY_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KWebPluginFactory> + + +class REKONQ_TESTS_EXPORT WebPluginFactory : public KWebPluginFactory +{ + Q_OBJECT + +public: + WebPluginFactory(QObject *parent); + + virtual QObject *create(const QString &mimeType, + const QUrl &url, + const QStringList &argumentNames, + const QStringList &argumentValues) const; + +Q_SIGNALS: + void signalLoadClickToFlash(bool) const; + +public Q_SLOTS: + void setLoadClickToFlash(bool load); + +private: + /** + When true, force loading of next flash animation (don't show clicktoflash) + We use signals/slots to set this property because QWebPluginFactory::create is const + */ + bool _loadClickToFlash; +}; + +#endif // WEB_PLUGIN_FACTORY_H diff --git a/src/webtab/websslinfo.cpp b/src/webtab/websslinfo.cpp new file mode 100644 index 00000000..adbf64f5 --- /dev/null +++ b/src/webtab/websslinfo.cpp @@ -0,0 +1,232 @@ +/* ============================================================ +* +* This file is a part of the KDE project +* +* Copyright (C) 2009-2011 by Dawit Alemayehu <adawit@kde.org> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "websslinfo.h" + +// Qt Includes +#include <QtCore/QVariant> + + +class WebSslInfo::WebSslInfoPrivate +{ +public: + WebSslInfoPrivate() + : usedCipherBits(0), supportedCipherBits(0) {} + + QUrl url; + QString ciphers; + QString protocol; + QString certErrors; + QHostAddress peerAddress; + QHostAddress parentAddress; + QList<QSslCertificate> certificateChain; + + int usedCipherBits; + int supportedCipherBits; +}; + +WebSslInfo::WebSslInfo() + : d(new WebSslInfo::WebSslInfoPrivate) +{ +} + +WebSslInfo::WebSslInfo(const WebSslInfo& other) + : d(new WebSslInfo::WebSslInfoPrivate) +{ + *this = other; +} + +WebSslInfo::~WebSslInfo() +{ + delete d; + d = 0; +} + +bool WebSslInfo::isValid() const +{ + return (d ? !d->peerAddress.isNull() : false); +} + +QUrl WebSslInfo::url() const +{ + return (d ? d->url : QUrl()); +} + +QHostAddress WebSslInfo::parentAddress() const +{ + return (d ? d->parentAddress : QHostAddress()); +} + +QHostAddress WebSslInfo::peerAddress() const +{ + return (d ? d->peerAddress : QHostAddress()); +} + +QString WebSslInfo::protocol() const +{ + return (d ? d->protocol : QString()); +} + +QString WebSslInfo::ciphers() const +{ + return (d ? d->ciphers : QString()); +} + +QString WebSslInfo::certificateErrors() const +{ + return (d ? d->certErrors : QString()); +} + +int WebSslInfo::supportedChiperBits() const +{ + return (d ? d->supportedCipherBits : 0); +} + +int WebSslInfo::usedChiperBits() const +{ + return (d ? d->usedCipherBits : 0); +} + +QList<QSslCertificate> WebSslInfo::certificateChain() const +{ + return (d ? d->certificateChain : QList<QSslCertificate>()); +} + +WebSslInfo& WebSslInfo::operator=(const WebSslInfo & other) +{ + if (d) + { + d->ciphers = other.d->ciphers; + d->protocol = other.d->protocol; + d->certErrors = other.d->certErrors; + d->peerAddress = other.d->peerAddress; + d->parentAddress = other.d->parentAddress; + d->certificateChain = other.d->certificateChain; + + d->usedCipherBits = other.d->usedCipherBits; + d->supportedCipherBits = other.d->supportedCipherBits; + d->url = other.d->url; + } + + return *this; +} + +bool WebSslInfo::saveTo(QMap<QString, QVariant>& data) const +{ + const bool ok = isValid(); + if (ok) + { + data.insert("ssl_in_use", true); + data.insert("ssl_peer_ip", d->peerAddress.toString()); + data.insert("ssl_parent_ip", d->parentAddress.toString()); + data.insert("ssl_protocol_version", d->protocol); + data.insert("ssl_cipher", d->ciphers); + data.insert("ssl_cert_errors", d->certErrors); + data.insert("ssl_cipher_used_bits", d->usedCipherBits); + data.insert("ssl_cipher_bits", d->supportedCipherBits); + QByteArray certChain; + Q_FOREACH(const QSslCertificate & cert, d->certificateChain) + certChain += cert.toPem(); + data.insert("ssl_peer_chain", certChain); + } + + return ok; +} + +void WebSslInfo::restoreFrom(const QVariant& value, const QUrl& url) +{ + if (value.isValid() && value.type() == QVariant::Map) + { + QMap<QString, QVariant> metaData = value.toMap(); + if (metaData.value("ssl_in_use", false).toBool()) + { + setCertificateChain(metaData.value("ssl_peer_chain").toByteArray()); + setPeerAddress(metaData.value("ssl_peer_ip").toString()); + setParentAddress(metaData.value("ssl_parent_ip").toString()); + setProtocol(metaData.value("ssl_protocol_version").toString()); + setCiphers(metaData.value("ssl_cipher").toString()); + setCertificateErrors(metaData.value("ssl_cert_errors").toString()); + setUsedCipherBits(metaData.value("ssl_cipher_used_bits").toString()); + setSupportedCipherBits(metaData.value("ssl_cipher_bits").toString()); + setUrl(url); + } + } +} + +void WebSslInfo::setUrl(const QUrl &url) +{ + if (d) + d->url = url; +} + +void WebSslInfo::setPeerAddress(const QString& address) +{ + if (d) + d->peerAddress = address; +} + +void WebSslInfo::setParentAddress(const QString& address) +{ + if (d) + d->parentAddress = address; +} + +void WebSslInfo::setProtocol(const QString& protocol) +{ + if (d) + d->protocol = protocol; +} + +void WebSslInfo::setCertificateChain(const QByteArray& chain) +{ + if (d) + d->certificateChain = QSslCertificate::fromData(chain); +} + +void WebSslInfo::setCiphers(const QString& ciphers) +{ + if (d) + d->ciphers = ciphers; +} + +void WebSslInfo::setUsedCipherBits(const QString& bits) +{ + if (d) + d->usedCipherBits = bits.toInt(); +} + +void WebSslInfo::setSupportedCipherBits(const QString& bits) +{ + if (d) + d->supportedCipherBits = bits.toInt(); +} + +void WebSslInfo::setCertificateErrors(const QString& certErrors) +{ + if (d) + d->certErrors = certErrors; +} diff --git a/src/webtab/websslinfo.h b/src/webtab/websslinfo.h new file mode 100644 index 00000000..6d47b4c8 --- /dev/null +++ b/src/webtab/websslinfo.h @@ -0,0 +1,78 @@ +/* ============================================================ +* +* This file is a part of the KDE project +* +* Copyright (C) 2009-2011 by Dawit Alemayehu <adawit@kde.org> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WEBSSLINFO_H +#define WEBSSLINFO_H + +#include <kdemacros.h> + +#include <QtCore/QUrl> +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtNetwork/QHostAddress> +#include <QtNetwork/QSslCertificate> + + +class WebSslInfo +{ +public: + WebSslInfo(); + WebSslInfo(const WebSslInfo&); + virtual ~WebSslInfo(); + + bool isValid() const; + QUrl url() const; + QHostAddress peerAddress() const; + QHostAddress parentAddress() const; + QString ciphers() const; + QString protocol() const; + QString certificateErrors() const; + int supportedChiperBits() const; + int usedChiperBits() const; + QList<QSslCertificate> certificateChain() const; + + bool saveTo(QMap<QString, QVariant>&) const; + void restoreFrom(const QVariant &, const QUrl& = QUrl()); + + void setUrl(const QUrl &url); + WebSslInfo& operator = (const WebSslInfo&); + +protected: + void setCiphers(const QString& ciphers); + void setProtocol(const QString& protocol); + void setPeerAddress(const QString& address); + void setParentAddress(const QString& address); + void setCertificateChain(const QByteArray& chain); + void setCertificateErrors(const QString& certErrors); + void setUsedCipherBits(const QString& bits); + void setSupportedCipherBits(const QString& bits); + +private: + class WebSslInfoPrivate; + WebSslInfoPrivate* d; +}; + +#endif // WEBSSLINFO_H diff --git a/src/webtab/webtab.cpp b/src/webtab/webtab.cpp new file mode 100644 index 00000000..457e9006 --- /dev/null +++ b/src/webtab/webtab.cpp @@ -0,0 +1,419 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "webtab.h" +#include "webtab.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "historymanager.h" +#include "messagebar.h" +#include "opensearchmanager.h" +#include "previewselectorbar.h" +#include "rsswidget.h" +#include "searchenginebar.h" +#include "sessionmanager.h" +#include "syncmanager.h" +#include "urlbar.h" +#include "walletbar.h" +#include "webpage.h" +#include "websnap.h" +#include "webshortcutwidget.h" + +// KDE Includes +#include <KWebWallet> +#include <KStandardShortcut> +#include <KMenu> +#include <KActionMenu> +#include <KWebView> +#include <KDebug> +#include <KBuildSycocaProgressDialog> + +// Qt Includes +#include <QVBoxLayout> + + +WebTab::WebTab(QWidget *parent) + : QWidget(parent) + , m_webView(0) + , m_urlBar(new UrlBar(this)) + , m_progress(0) + , m_part(0) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + QVBoxLayout *l = new QVBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + + l->addWidget(view()); + view()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // fix focus handling + setFocusProxy(view()); + + KWebWallet *wallet = page()->wallet(); + + if (wallet) + { + connect(wallet, SIGNAL(saveFormDataRequested(QString, QUrl)), + this, SLOT(createWalletBar(QString, QUrl))); + } + + connect(view(), SIGNAL(loadProgress(int)), this, SLOT(updateProgress(int))); + connect(view(), SIGNAL(loadStarted()), this, SLOT(resetProgress())); + connect(view(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString))); + connect(view(), SIGNAL(loadFinished(bool)), this, SLOT(loadFinished())); + + // Session Manager + connect(view(), SIGNAL(loadFinished(bool)), rApp->sessionManager(), SLOT(saveSession())); +} + + +WebTab::~WebTab() +{ + m_walletBar.clear(); + m_previewSelectorBar.clear(); + + // NOTE: + // Urlbar is reparented when inserted in StackedUrlBar, so we need + // to get sure it will be deleted. Deleting it later to ensure everything + // will be finished before ;) + m_urlBar->deleteLater(); + + // Get sure m_part will be deleted + delete m_part; +} + + +WebView *WebTab::view() +{ + if (!m_webView) + { + m_webView = new WebView(this); + } + return m_webView; +} + + +WebPage *WebTab::page() +{ + if (view()) + return view()->page(); + + return 0; +} + + +KUrl WebTab::url() +{ + if (page() && page()->isOnRekonqPage()) + { + return page()->loadingUrl(); + } + + if (view()) + return view()->url(); + + kDebug() << "OOPS... NO web classes survived! Returning an empty url..."; + return KUrl(); +} + + +void WebTab::updateProgress(int p) +{ + m_progress = p; + emit loadProgressing(); +} + + +void WebTab::resetProgress() +{ + m_progress = 1; +} + + +bool WebTab::isPageLoading() +{ + return m_progress != 0 && m_progress != 100; +} + + +void WebTab::createWalletBar(const QString &key, const QUrl &url) +{ + // check if the url is in the wallet blacklist + QString urlString = url.toString(); + QStringList blackList = ReKonfig::walletBlackList(); + if (blackList.contains(urlString)) + return; + + KWebWallet *wallet = page()->wallet(); + + if (!ReKonfig::passwordSavingEnabled()) + { + wallet->rejectSaveFormDataRequest(key); + return; + } + + if (m_walletBar.isNull()) + { + m_walletBar = new WalletBar(this); + m_walletBar.data()->onSaveFormData(key, url); + qobject_cast<QVBoxLayout *>(layout())->insertWidget(0, m_walletBar.data()); + m_walletBar.data()->animatedShow(); + } + else + { + disconnect(wallet); + qobject_cast<QVBoxLayout *>(layout())->insertWidget(0, m_walletBar.data()); + m_walletBar.data()->animatedShow(); + } + + connect(m_walletBar.data(), SIGNAL(saveFormDataAccepted(QString)), + wallet, SLOT(acceptSaveFormDataRequest(QString)), Qt::UniqueConnection); + connect(m_walletBar.data(), SIGNAL(saveFormDataRejected(QString)), + wallet, SLOT(rejectSaveFormDataRequest(QString)), Qt::UniqueConnection); + + // sync passwords + connect(m_walletBar.data(), SIGNAL(saveFormDataAccepted(QString)), + rApp->syncManager(), SLOT(syncPasswords()), Qt::UniqueConnection); +} + + +void WebTab::createPreviewSelectorBar(int index) +{ + if (m_previewSelectorBar.isNull()) + { + m_previewSelectorBar = new PreviewSelectorBar(index, this); + qobject_cast<QVBoxLayout *>(layout())->insertWidget(0, m_previewSelectorBar.data()); + m_previewSelectorBar.data()->animatedShow(); + } + else + { + disconnect(m_previewSelectorBar.data()); + m_previewSelectorBar.data()->setIndex(index); + m_previewSelectorBar.data()->animatedHide(); + } + + connect(page(), SIGNAL(loadStarted()), m_previewSelectorBar.data(), SLOT(loadProgress()), Qt::UniqueConnection); + connect(page(), SIGNAL(loadProgress(int)), m_previewSelectorBar.data(), SLOT(loadProgress()), Qt::UniqueConnection); + connect(page(), SIGNAL(loadFinished(bool)), m_previewSelectorBar.data(), SLOT(loadFinished()), Qt::UniqueConnection); + connect(page()->mainFrame(), SIGNAL(urlChanged(QUrl)), m_previewSelectorBar.data(), SLOT(verifyUrl()), Qt::UniqueConnection); +} + + +bool WebTab::hasRSSInfo() +{ + QWebElementCollection col = page()->mainFrame()->findAllElements("link[type=\"application/rss+xml\"]"); + col.append(page()->mainFrame()->findAllElements("link[type=\"application/atom+xml\"]")); + if (col.count() != 0) + return true; + + return false; +} + + +void WebTab::showRSSInfo(const QPoint &pos) +{ + QWebElementCollection col = page()->mainFrame()->findAllElements("link[type=\"application/rss+xml\"]"); + col.append(page()->mainFrame()->findAllElements("link[type=\"application/atom+xml\"]")); + + QMap<KUrl, QString> map; + + Q_FOREACH(const QWebElement & el, col) + { + QString urlString; + if (el.attribute("href").startsWith(QL1S("http"))) + urlString = el.attribute("href"); + else + { + KUrl u = url(); + // NOTE + // cd() is probably better than setPath() here, + // for all those url sites just having a path + if (u.cd(el.attribute("href"))) + urlString = u.toMimeDataString(); + } + + QString title = el.attribute("title"); + if (title.isEmpty()) + title = el.attribute("href"); + + map.insert(KUrl(urlString), title); + } + + RSSWidget *widget = new RSSWidget(map, window()); + widget->showAt(pos); +} + + +void WebTab::hideSelectorBar() +{ + m_previewSelectorBar.data()->animatedHide(); +} + + +void WebTab::setPart(KParts::ReadOnlyPart *p, const KUrl &u) +{ + if (p) + { + // Ok, part exists. Insert & show it.. + m_part = p; + qobject_cast<QVBoxLayout *>(layout())->insertWidget(1, p->widget()); + p->openUrl(u); + m_webView->hide(); + + emit titleChanged(u.url()); + return; + } + + if (!m_part) + return; + + // Part NO more exists. Let's clean up from webtab + m_webView->show(); + qobject_cast<QVBoxLayout *>(layout())->removeWidget(m_part->widget()); + delete m_part; + m_part = 0; +} + + +KUrl WebTab::extractOpensearchUrl(QWebElement e) +{ + QString href = e.attribute(QL1S("href")); + KUrl url = KUrl(href); + if (!href.contains(":")) + { + KUrl docUrl = m_webView->url(); + QString host = docUrl.scheme() + "://" + docUrl.host(); + if (docUrl.port() != -1) + { + host += QL1C(':') + QString::number(docUrl.port()); + } + url = KUrl(docUrl, href); + } + return url; +} + + +bool WebTab::hasNewSearchEngine() +{ + QWebElement e = page()->mainFrame()->findFirstElement(QL1S("head >link[rel=\"search\"][ type=\"application/opensearchdescription+xml\"]")); + return !e.isNull() && !rApp->opensearchManager()->engineExists(extractOpensearchUrl(e)); +} + + +void WebTab::showSearchEngine(const QPoint &pos) +{ + QWebElement e = page()->mainFrame()->findFirstElement(QL1S("head >link[rel=\"search\"][ type=\"application/opensearchdescription+xml\"]")); + QString title = e.attribute(QL1S("title")); + if (!title.isEmpty()) + { + WebShortcutWidget *widget = new WebShortcutWidget(window()); + widget->setWindowFlags(Qt::Popup); + + connect(widget, SIGNAL(webShortcutSet(KUrl, QString, QString)), + rApp->opensearchManager(), SLOT(addOpenSearchEngine(KUrl, QString, QString))); + connect(rApp->opensearchManager(), SIGNAL(openSearchEngineAdded(QString)), + this, SLOT(openSearchEngineAdded())); + + widget->show(extractOpensearchUrl(e), title, pos); + } +} + + +void WebTab::openSearchEngineAdded() +{ + // If the providers changed, tell sycoca to rebuild its database... + KBuildSycocaProgressDialog::rebuildKSycoca(this); + + disconnect(rApp->opensearchManager(), SIGNAL(openSearchEngineAdded(QString, QString, QString)), + this, SLOT(openSearchEngineAdded())); +} + + +void WebTab::showMessageBar() +{ + MessageBar *msgBar = new MessageBar(i18n("It seems rekonq was not closed properly. Do you want " + "to restore the last saved session?"), this); + + qobject_cast<QVBoxLayout *>(layout())->insertWidget(0, msgBar); + msgBar->animatedShow(); + + connect(msgBar, SIGNAL(accepted()), rApp->sessionManager(), SLOT(restoreCrashedSession())); +} + + +bool WebTab::hasAdBlockedElements() +{ + return page()->hasAdBlockedElements(); +} + + +QPixmap WebTab::tabPreview(int width, int height) +{ + if (isPageLoading()) + { + // no previews during load + return QPixmap(); + } + + if (!part()) + { + return WebSnap::renderPagePreview(*page(), width, height); + } + else + { + QWidget *partWidget = part()->widget(); + QPixmap partThumb(partWidget->size()); + + partWidget->render(&partThumb); + + return partThumb.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } +} + + +void WebTab::loadFinished() +{ + // add page to history + QString pageTitle = (page() && page()->isOnRekonqPage()) ? url().url() : m_webView->title(); + rApp->historyManager()->addHistoryEntry(url(), pageTitle); +} + + +void WebTab::showSearchEngineBar() +{ + SearchEngineBar *seBar = new SearchEngineBar(this); + + qobject_cast<QVBoxLayout *>(layout())->insertWidget(0, seBar); + seBar->animatedShow(); +} diff --git a/src/webtab/webtab.h b/src/webtab/webtab.h new file mode 100644 index 00000000..39c23fb9 --- /dev/null +++ b/src/webtab/webtab.h @@ -0,0 +1,131 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WEBTAB_H +#define WEBTAB_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Local Includes +#include "webview.h" + +// KDE Includes +#include <KParts/Part> + +// Qt Includes +#include <QWidget> + +// Forward Declarations +class NotificationBar; +class PreviewSelectorBar; +class QPoint; +class UrlBar; +class WalletBar; +class WebPage; + + +class REKONQ_TESTS_EXPORT WebTab : public QWidget +{ + Q_OBJECT + +public: + explicit WebTab(QWidget *parent = 0); + ~WebTab(); + + WebView *view(); + WebPage *page(); + + inline UrlBar *urlBar() const + { + return m_urlBar; + } + + inline int progress() const + { + return m_progress; + } + + KUrl url(); + + void createPreviewSelectorBar(int index); + + void hideSelectorBar(); + + bool hasRSSInfo(); + + bool isPageLoading(); + + bool hasNewSearchEngine(); + + KParts::ReadOnlyPart *part() + { + return m_part; + } + + void setPart(KParts::ReadOnlyPart *p, const KUrl &u); + + bool hasAdBlockedElements(); + + QPixmap tabPreview(int width, int height); + +private Q_SLOTS: + void updateProgress(int progress); + void resetProgress(); + + void createWalletBar(const QString &, const QUrl &); + void showRSSInfo(const QPoint &pos); + void showSearchEngine(const QPoint &pos); + void openSearchEngineAdded(); + + void showMessageBar(); + void loadFinished(); + + void showSearchEngineBar(); + +private: + KUrl extractOpensearchUrl(QWebElement e); + +Q_SIGNALS: + void loadProgressing(); + void titleChanged(const QString &); + +private: + WebView *m_webView; + + UrlBar *m_urlBar; + + int m_progress; + + QWeakPointer<WalletBar> m_walletBar; + QWeakPointer<PreviewSelectorBar> m_previewSelectorBar; + + KParts::ReadOnlyPart *m_part; +}; + +#endif diff --git a/src/webtab/webview.cpp b/src/webtab/webview.cpp new file mode 100644 index 00000000..00112d61 --- /dev/null +++ b/src/webtab/webview.cpp @@ -0,0 +1,1273 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +// Self Includes +#include "webview.h" +#include "webview.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" +#include "adblockmanager.h" +#include "bookmarkmanager.h" +#include "iconmanager.h" +#include "mainview.h" +#include "mainwindow.h" +#include "searchengine.h" +#include "urlbar.h" +#include "webpage.h" +#include "webtab.h" + +// KDE Includes +#include <KAction> +#include <KActionMenu> +#include <KLocalizedString> +#include <KMenu> +#include <KStandardAction> +#include <KStandardDirs> +#include <KToolInvocation> + +// Qt Includes +#include <QFile> +#include <QTimer> + +#include <QClipboard> +#include <QContextMenuEvent> +#include <QLabel> + +#include <QWebFrame> +#include <QWebHistory> +#include <QNetworkRequest> + + +WebView::WebView(QWidget* parent) + : KWebView(parent, false) + , m_page(0) + , m_autoScrollTimer(new QTimer(this)) + , m_verticalAutoScrollSpeed(0) + , m_horizontalAutoScrollSpeed(0) + , m_isViewAutoScrolling(false) + , m_autoScrollIndicator(QPixmap(KStandardDirs::locate("appdata" , "pics/autoscroll.png"))) + , m_smoothScrollTimer(new QTimer(this)) + , m_dy(0) + , m_smoothScrollSteps(0) + , m_isViewSmoothScrolling(false) + , m_accessKeysPressed(false) + , m_accessKeysActive(false) +{ + // loadUrl signal + connect(this, SIGNAL(loadUrl(KUrl, Rekonq::OpenType)), rApp, SLOT(loadUrl(KUrl, Rekonq::OpenType))); + + // Auto scroll timer + connect(m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(scrollFrameChanged())); + m_autoScrollTimer->setInterval(100); + + // Smooth scroll timer + connect(m_smoothScrollTimer, SIGNAL(timeout()), this, SLOT(scrollTick())); + m_smoothScrollTimer->setInterval(16); + + connect(this, SIGNAL(iconChanged()), this, SLOT(changeWindowIcon())); + connect(this, SIGNAL(loadStarted()), this, SLOT(loadStarted())); +} + + +WebView::~WebView() +{ + if (m_isViewSmoothScrolling) + stopSmoothScrolling(); +} + + +void WebView::load(const QUrl &url) +{ + load(QNetworkRequest(url)); +} + + +void WebView::load(const QNetworkRequest &req, QNetworkAccessManager::Operation op, const QByteArray &body) +{ + QNetworkRequest request = req; + const QUrl &reqUrl = request.url(); + if (reqUrl.host() == url().host()) + { + request.setRawHeader(QByteArray("Referer"), url().toEncoded()); + } + + KWebView::load(request, op, body); +} + + +void WebView::loadStarted() +{ + hideAccessKeys(); +} + + +void WebView::changeWindowIcon() +{ + if (ReKonfig::useFavicon()) + { + MainWindow *const mainWindow = rApp->mainWindow(); + if (url() == mainWindow->currentTab()->url()) + { + const int index = mainWindow->mainView()->currentIndex(); + mainWindow->changeWindowIcon(index); + } + } +} + + +WebPage *WebView::page() +{ + if (!m_page) + { + m_page = new WebPage(this); + setPage(m_page); + } + return m_page; +} + + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); + MainWindow *mainwindow = rApp->mainWindow(); + + KMenu menu(this); + QAction *a; + + KAction *inspectAction = new KAction(KIcon("layer-visible-on"), i18n("Inspect Element"), this); + connect(inspectAction, SIGNAL(triggered(bool)), this, SLOT(inspect())); + + KAction *sendByMailAction = new KAction(this); + sendByMailAction->setIcon(KIcon("mail-send")); + connect(sendByMailAction, SIGNAL(triggered(bool)), this, SLOT(sendByMail())); + + // Choose right context + int resultHit = 0; + if (result.linkUrl().isEmpty()) + resultHit = WebView::EmptySelection; + else + resultHit = WebView::LinkSelection; + + if (!result.pixmap().isNull()) + resultHit |= WebView::ImageSelection; + + if (result.isContentSelected()) + resultHit = WebView::TextSelection; + + // -------------------------------------------------------------------------------- + // Ok, let's start filling up the menu... + + // is content editable? Add PASTE + if (result.isContentEditable()) + { + menu.addAction(pageAction(KWebPage::Paste)); + menu.addSeparator(); + } + + // EMPTY PAGE ACTIONS ------------------------------------------------------------- + if (resultHit == WebView::EmptySelection) + { + // send by mail: page url + sendByMailAction->setData(page()->currentFrame()->url()); + sendByMailAction->setText(i18n("Share page url")); + + // navigation + QWebHistory *history = page()->history(); + if (history->canGoBack()) + { + menu.addAction(pageAction(KWebPage::Back)); + } + + if (history->canGoForward()) + { + menu.addAction(pageAction(KWebPage::Forward)); + } + + menu.addAction(mainwindow->actionByName("view_redisplay")); + + menu.addSeparator(); + + //Frame + KActionMenu *frameMenu = new KActionMenu(i18n("Current Frame"), this); + frameMenu->addAction(pageAction(KWebPage::OpenFrameInNewWindow)); + + a = new KAction(KIcon("document-print-frame"), i18n("Print Frame"), this); + connect(a, SIGNAL(triggered()), this, SLOT(printFrame())); + frameMenu->addAction(a); + + menu.addAction(frameMenu); + + menu.addSeparator(); + + // Page Actions + menu.addAction(pageAction(KWebPage::SelectAll)); + + menu.addAction(mainwindow->actionByName(KStandardAction::name(KStandardAction::SaveAs))); + + if (!KStandardDirs::findExe("kget").isNull() && ReKonfig::kgetList()) + { + a = new KAction(KIcon("kget"), i18n("List All Links"), this); + connect(a, SIGNAL(triggered(bool)), page(), SLOT(downloadAllContentsWithKGet())); + menu.addAction(a); + } + + menu.addAction(mainwindow->actionByName("page_source")); + menu.addAction(inspectAction); + + if (mainwindow->isFullScreen()) + { + menu.addSeparator(); + menu.addAction(mainwindow->actionByName("fullscreen")); + } + } + + // LINK ACTIONS ------------------------------------------------------------------- + if (resultHit & WebView::LinkSelection) + { + // send by mail: link url + sendByMailAction->setData(result.linkUrl()); + sendByMailAction->setText(i18n("Share link")); + + a = new KAction(KIcon("tab-new"), i18n("Open in New &Tab"), this); + a->setData(result.linkUrl()); + connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab())); + menu.addAction(a); + + a = new KAction(KIcon("window-new"), i18n("Open in New &Window"), this); + a->setData(result.linkUrl()); + connect(a, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); + menu.addAction(a); + + menu.addSeparator(); + + // Don't show dots if we are NOT going to ask for download path + a = pageAction(KWebPage::DownloadLinkToDisk); + if (ReKonfig::askDownloadPath()) + a->setText(i18n("Save Link...")); + else + a->setText(i18n("Save Link")); + + menu.addAction(a); + menu.addAction(pageAction(KWebPage::CopyLinkToClipboard)); + } + + // IMAGE ACTIONS ------------------------------------------------------------------ + if (resultHit & WebView::ImageSelection) + { + // send by mail: image url + sendByMailAction->setData(result.imageUrl()); + sendByMailAction->setText(i18n("Share image link")); + + menu.addSeparator(); + + a = new KAction(KIcon("view-preview"), i18n("&View Image"), this); + a->setData(result.imageUrl()); + connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), + this, SLOT(viewImage(Qt::MouseButtons, Qt::KeyboardModifiers))); + menu.addAction(a); + + menu.addAction(pageAction(KWebPage::DownloadImageToDisk)); + + a = new KAction(KIcon("view-media-visualization"), i18n("&Copy Image Location"), this); + a->setData(result.imageUrl()); + connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(slotCopyImageLocation())); + menu.addAction(a); + + if (rApp->adblockManager()->isEnabled()) + { + a = new KAction(KIcon("preferences-web-browser-adblock"), i18n("Block image"), this); + a->setData(result.imageUrl()); + connect(a, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(blockImage())); + menu.addAction(a); + } + } + + // ACTIONS FOR TEXT SELECTION ----------------------------------------------------- + if (resultHit & WebView::TextSelection) + { + // send by mail: text + sendByMailAction->setData(selectedText()); + sendByMailAction->setText(i18n("Share selected text")); + + if (result.isContentEditable()) + { + // actions for text selected in field + menu.addAction(pageAction(KWebPage::Cut)); + } + + a = pageAction(KWebPage::Copy); + if (!result.linkUrl().isEmpty()) + a->setText(i18n("Copy Text")); //for link + else + a->setText(i18n("Copy")); + menu.addAction(a); + + if (selectedText().contains('.') && selectedText().indexOf('.') < selectedText().length() + && !selectedText().trimmed().contains(" ") + ) + { + QString text = selectedText(); + text = text.trimmed(); + KUrl urlLikeText(text); + if (urlLikeText.isValid()) + { + QString truncatedUrl = text; + const int maxTextSize = 18; + if (truncatedUrl.length() > maxTextSize) + { + const int truncateSize = 15; + truncatedUrl.truncate(truncateSize); + truncatedUrl += QL1S("..."); + } + //open selected text url in a new tab + QAction * const openInNewTabAction = new KAction(KIcon("tab-new"), i18n("Open '%1' in New Tab", truncatedUrl), this); + openInNewTabAction->setData(QUrl(urlLikeText)); + connect(openInNewTabAction, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewTab())); + menu.addAction(openInNewTabAction); + //open selected text url in a new window + QAction * const openInNewWindowAction = new KAction(KIcon("window-new"), i18n("Open '%1' in New Window", truncatedUrl), this); + openInNewWindowAction->setData(QUrl(urlLikeText)); + connect(openInNewWindowAction, SIGNAL(triggered(bool)), this, SLOT(openLinkInNewWindow())); + menu.addAction(openInNewWindowAction); + menu.addSeparator(); + } + } + + //Default SearchEngine + KService::Ptr defaultEngine = SearchEngine::defaultEngine(); + if (defaultEngine) // check if a default engine is set + { + a = new KAction(i18nc("Search selected text with the default search engine", "Search with %1", defaultEngine->name()), this); + a->setIcon(rApp->iconManager()->iconForUrl(SearchEngine::buildQuery(defaultEngine, ""))); + a->setData(defaultEngine->entryPath()); + connect(a, SIGNAL(triggered(bool)), this, SLOT(search())); + menu.addAction(a); + } + + //All favourite ones + KActionMenu *searchMenu = new KActionMenu(KIcon("edit-find"), i18nc("@title:menu", "Search"), this); + + Q_FOREACH(const KService::Ptr & engine, SearchEngine::favorites()) + { + a = new KAction(i18nc("@item:inmenu Search, %1 = search engine", "With %1", engine->name()), this); + a->setIcon(rApp->iconManager()->iconForUrl(SearchEngine::buildQuery(engine, ""))); + a->setData(engine->entryPath()); + connect(a, SIGNAL(triggered(bool)), this, SLOT(search())); + searchMenu->addAction(a); + } + + a = new KAction(KIcon("edit-find"), i18n("On Current Page"), this); + connect(a, SIGNAL(triggered()), rApp->mainWindow(), SLOT(findSelectedText())); + searchMenu->addAction(a); + + if (!searchMenu->menu()->isEmpty()) + { + menu.addAction(searchMenu); + } + } + + // DEFAULT ACTIONs (on the bottom) ------------------------------------------------ + menu.addSeparator(); + if (resultHit & WebView::LinkSelection) + { + a = new KAction(KIcon("bookmark-new"), i18n("&Bookmark link"), this); + a->setData(result.linkUrl()); + connect(a, SIGNAL(triggered(bool)), this, SLOT(bookmarkLink())); + menu.addAction(a); + } + else + { + a = mainwindow->actionByName(KStandardAction::name(KStandardAction::AddBookmark)); + menu.addAction(a); + } + menu.addAction(sendByMailAction); + menu.addAction(inspectAction); + + // finally launch the menu... + menu.exec(mapToGlobal(event->pos())); +} + + +void WebView::mousePressEvent(QMouseEvent *event) +{ + if (m_isViewAutoScrolling) + { + m_verticalAutoScrollSpeed = 0; + m_horizontalAutoScrollSpeed = 0; + m_autoScrollTimer->stop(); + m_isViewAutoScrolling = false; + update(); + return; + } + + QWebHitTestResult result = page()->mainFrame()->hitTestContent(event->pos()); + bool weCanDoMiddleClickActions = !result.isContentEditable() && result.linkUrl().isEmpty(); + + switch (event->button()) + { + case Qt::XButton1: + triggerPageAction(KWebPage::Back); + break; + + case Qt::XButton2: + triggerPageAction(KWebPage::Forward); + break; + + case Qt::MidButton: + switch (ReKonfig::middleClickAction()) + { + case 0: // AutoScroll + if (weCanDoMiddleClickActions + && !m_isViewAutoScrolling + && !page()->currentFrame()->scrollBarGeometry(Qt::Horizontal).contains(event->pos()) + && !page()->currentFrame()->scrollBarGeometry(Qt::Vertical).contains(event->pos())) + { + if (!page()->currentFrame()->scrollBarGeometry(Qt::Horizontal).isNull() + || !page()->currentFrame()->scrollBarGeometry(Qt::Vertical).isNull()) + { + m_clickPos = event->pos(); + m_isViewAutoScrolling = true; + update(); + } + } + break; + + case 1: // Load Clipboard URL + if (weCanDoMiddleClickActions) + { + const QString clipboardContent = rApp->clipboard()->text(); + + if (clipboardContent.isEmpty()) + break; + + if (QUrl::fromUserInput(clipboardContent).isValid()) + loadUrl(clipboardContent, Rekonq::CurrentTab); + else // Search with default Engine + { + KService::Ptr defaultEngine = SearchEngine::defaultEngine(); + if (defaultEngine) // check if a default engine is set + loadUrl(KUrl(SearchEngine::buildQuery(defaultEngine, clipboardContent)), Rekonq::CurrentTab); + } + } + break; + + default: // Do Nothing + break; + } + break; + + default: + break; + }; + + KWebView::mousePressEvent(event); +} + + +void WebView::mouseMoveEvent(QMouseEvent *event) +{ + QPoint mousePos = event->pos(); + + if (m_isViewAutoScrolling) + { + QPoint r = mousePos - m_clickPos; + m_horizontalAutoScrollSpeed = r.x() / 2; // you are too fast.. + m_verticalAutoScrollSpeed = r.y() / 2; + if (!m_autoScrollTimer->isActive()) + m_autoScrollTimer->start(); + + return; + } + + MainWindow *w = rApp->mainWindow(); + if (w->isFullScreen()) + { + if (event->pos().y() >= 0 && event->pos().y() <= 4) + { + w->setWidgetsVisible(true); + } + else + { + if (!w->mainView()->currentUrlBar()->hasFocus()) + w->setWidgetsVisible(false); + } + } + KWebView::mouseMoveEvent(event); +} + + +void WebView::dropEvent(QDropEvent *event) +{ + bool isEditable = page()->frameAt(event->pos())->hitTestContent(event->pos()).isContentEditable(); + if (event->mimeData()->hasFormat(BookmarkManager::bookmark_mime_type())) + { + QByteArray addresses = event->mimeData()->data(BookmarkManager::bookmark_mime_type()); + KBookmark bookmark = rApp->bookmarkManager()->findByAddress(QString::fromLatin1(addresses.data())); + if (bookmark.isGroup()) + { + rApp->bookmarkManager()->openFolderinTabs(bookmark.toGroup()); + } + else + { + emit loadUrl(bookmark.url(), Rekonq::CurrentTab); + } + } + else if (event->mimeData()->hasUrls() && event->source() != this && !isEditable) //dropped links + { + Q_FOREACH(const QUrl & url, event->mimeData()->urls()) + { + emit loadUrl(url, Rekonq::NewFocusedTab); + } + } + else if (event->mimeData()->hasFormat("text/plain") && event->source() != this && !isEditable) //dropped plain text with url format + { + QUrl url = QUrl::fromUserInput(event->mimeData()->data("text/plain")); + + if (url.isValid()) + emit loadUrl(url, Rekonq::NewFocusedTab); + } + else + { + KWebView::dropEvent(event); + } +} + + +void WebView::paintEvent(QPaintEvent* event) +{ + KWebView::paintEvent(event); + + if (m_isViewAutoScrolling) + { + QPoint centeredPoint = m_clickPos; + centeredPoint.setX(centeredPoint.x() - m_autoScrollIndicator.width() / 2); + centeredPoint.setY(centeredPoint.y() - m_autoScrollIndicator.height() / 2); + + QPainter painter(this); + painter.setOpacity(0.8); + painter.drawPixmap(centeredPoint, m_autoScrollIndicator); + } +} + + +void WebView::search() +{ + KAction *a = qobject_cast<KAction*>(sender()); + KService::Ptr engine = KService::serviceByDesktopPath(a->data().toString()); + KUrl urlSearch = KUrl(SearchEngine::buildQuery(engine, selectedText())); + + emit loadUrl(urlSearch, Rekonq::NewTab); +} + + +void WebView::printFrame() +{ + rApp->mainWindow()->printRequested(page()->currentFrame()); +} + + +void WebView::viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) +{ + KAction *a = qobject_cast<KAction*>(sender()); + KUrl url(a->data().toUrl()); + + if (modifiers & Qt::ControlModifier || buttons == Qt::MidButton) + { + emit loadUrl(url, Rekonq::NewTab); + } + else + { + emit loadUrl(url, Rekonq::CurrentTab); + } +} + + +void WebView::slotCopyImageLocation() +{ + KAction *a = qobject_cast<KAction*>(sender()); + KUrl imageUrl(a->data().toUrl()); +#ifndef QT_NO_MIMECLIPBOARD + // Set it in both the mouse selection and in the clipboard + QMimeData* mimeData = new QMimeData; + imageUrl.populateMimeData(mimeData); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); + mimeData = new QMimeData; + imageUrl.populateMimeData(mimeData); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Selection); +#else + QApplication::clipboard()->setText(imageUrl.url()); +#endif +} + + +void WebView::openLinkInNewWindow() +{ + KAction *a = qobject_cast<KAction*>(sender()); + KUrl url(a->data().toUrl()); + + emit loadUrl(url, Rekonq::NewWindow); +} + + +void WebView::openLinkInNewTab() +{ + KAction *a = qobject_cast<KAction*>(sender()); + KUrl url(a->data().toUrl()); + + emit loadUrl(url, Rekonq::NewTab); +} + + +void WebView::bookmarkLink() +{ + KAction *a = qobject_cast<KAction*>(sender()); + KUrl url(a->data().toUrl()); + + rApp->bookmarkManager()->rootGroup().addBookmark(url.prettyUrl(), url); + rApp->bookmarkManager()->emitChanged(); +} + + +void WebView::keyPressEvent(QKeyEvent *event) +{ + // If CTRL was hit, be prepared for access keys + if (ReKonfig::accessKeysEnabled() + && !m_accessKeysActive + && event->key() == Qt::Key_Control + && !(event->modifiers() & ~Qt::ControlModifier) + ) + { + m_accessKeysPressed = true; + event->accept(); + return; + } + + const QString tagName = page()->mainFrame()->evaluateJavaScript("document.activeElement.tagName").toString(); + + if (event->modifiers() == Qt::ControlModifier) + { + if (event->key() == Qt::Key_C) + { + triggerPageAction(KWebPage::Copy); + event->accept(); + return; + } + + if (event->key() == Qt::Key_A) + { + triggerPageAction(KWebPage::SelectAll); + event->accept(); + return; + } + + // CTRL + RETURN: open link into another tab + if (event->key() == Qt::Key_Return && tagName == QL1S("A")) + { + KUrl u = KUrl(page()->mainFrame()->evaluateJavaScript("document.activeElement.attributes[\"href\"].value").toString()); + emit loadUrl(u, Rekonq::NewTab); + event->accept(); + return; + } + } + + // Auto Scrolling + if (event->modifiers() == Qt::ShiftModifier + && tagName != QL1S("INPUT") + && tagName != QL1S("TEXTAREA") + ) + { + // NOTE and FIXME + // This check is doabled because it presents strange behavior: QtWebKit check works well in pages like gmail + // and fails on sites like g+. The opposite is true for javascript check. + // Please, help me finding the right way to check this EVERY TIME. + bool isContentEditableQW = page()->mainFrame()->hitTestContent(QCursor::pos()).isContentEditable(); + bool isContentEditableJS = page()->mainFrame()->evaluateJavaScript("document.activeElement.isContentEditable").toBool(); + + if (!isContentEditableQW && !isContentEditableJS) + { + if (event->key() == Qt::Key_Up) + { + m_verticalAutoScrollSpeed--; + if (!m_autoScrollTimer->isActive()) + m_autoScrollTimer->start(); + + event->accept(); + return; + } + + if (event->key() == Qt::Key_Down) + { + m_verticalAutoScrollSpeed++; + if (!m_autoScrollTimer->isActive()) + m_autoScrollTimer->start(); + + event->accept(); + return; + } + + if (event->key() == Qt::Key_Right) + { + m_horizontalAutoScrollSpeed++; + if (!m_autoScrollTimer->isActive()) + m_autoScrollTimer->start(); + + event->accept(); + return; + } + + if (event->key() == Qt::Key_Left) + { + m_horizontalAutoScrollSpeed--; + if (!m_autoScrollTimer->isActive()) + m_autoScrollTimer->start(); + + event->accept(); + return; + } + + if (m_autoScrollTimer->isActive()) + { + m_autoScrollTimer->stop(); + event->accept(); + return; + } + else + { + if (m_verticalAutoScrollSpeed || m_horizontalAutoScrollSpeed) + { + m_autoScrollTimer->start(); + event->accept(); + return; + } + } + } + + // if you arrived here, I hope it means SHIFT has been pressed NOT for autoscroll management... + } + + if (ReKonfig::accessKeysEnabled() && m_accessKeysActive) + { + hideAccessKeys(); + event->accept(); + return; + } + + // vi-like navigation + if (ReKonfig::enableViShortcuts()) + { + if (event->modifiers() == Qt::NoModifier + && tagName != QL1S("INPUT") + && tagName != QL1S("TEXTAREA") + ) + { + // See note up! + bool isContentEditableQW = page()->mainFrame()->hitTestContent(QCursor::pos()).isContentEditable(); + bool isContentEditableJS = page()->mainFrame()->evaluateJavaScript("document.activeElement.isContentEditable").toBool(); + + if (!isContentEditableQW && !isContentEditableJS) + { + switch (event->key()) + { + case Qt::Key_J: + event->accept(); + event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier); + break; + case Qt::Key_K: + event->accept(); + event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier); + break; + case Qt::Key_L: + event->accept(); + event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + break; + case Qt::Key_H: + event->accept(); + event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + break; + default: + break; + } + } + } + } + + KWebView::keyPressEvent(event); +} + + +void WebView::keyReleaseEvent(QKeyEvent *event) +{ + // access keys management + if (ReKonfig::accessKeysEnabled()) + { + if (m_accessKeysPressed && event->key() != Qt::Key_Control) + m_accessKeysPressed = false; + + if (m_accessKeysPressed && !(event->modifiers() & Qt::ControlModifier)) + { + kDebug() << "Shotting access keys"; + QTimer::singleShot(200, this, SLOT(accessKeyShortcut())); + event->accept(); + return; + } + else + { + checkForAccessKey(event); + kDebug() << "Hiding access keys"; + hideAccessKeys(); + event->accept(); + return; + } + } + + KWebView::keyReleaseEvent(event); +} + + +void WebView::wheelEvent(QWheelEvent *event) +{ + if (event->orientation() == Qt::Vertical || !ReKonfig::hScrollWheelHistory()) + { + // To let some websites (eg: google maps) to handle wheel events + int prevPos = page()->currentFrame()->scrollPosition().y(); + KWebView::wheelEvent(event); + int newPos = page()->currentFrame()->scrollPosition().y(); + + // Sync with the zoom slider + if (event->modifiers() == Qt::ControlModifier) + { + // Limits of the slider + if (zoomFactor() > 1.9) + setZoomFactor(1.9); + else if (zoomFactor() < 0.1) + setZoomFactor(0.1); + + // Round the factor (Fix slider's end value) + int newFactor = zoomFactor() * 10; + if ((zoomFactor() * 10 - newFactor) > 0.5) + newFactor++; + + emit zoomChanged(newFactor); + } + else if (ReKonfig::smoothScrolling() && prevPos != newPos) + { + page()->currentFrame()->setScrollPosition(QPoint(page()->currentFrame()->scrollPosition().x(), prevPos)); + + if ((event->delta() > 0) != !m_smoothScrollBottomReached) + stopSmoothScrolling(); + + if (event->delta() > 0) + m_smoothScrollBottomReached = false; + else + m_smoothScrollBottomReached = true; + + + setupSmoothScrolling(abs(newPos - prevPos)); + } + } + // use horizontal wheel events to go back and forward in tab history + else + { + // left -> go to previous page + if (event->delta() > 0) + { + emit openPreviousInHistory(); + } + // right -> go to next page + if (event->delta() < 0) + { + emit openNextInHistory(); + } + } +} + + +void WebView::inspect() +{ + QAction *a = rApp->mainWindow()->actionByName("web_inspector"); + if (a && !a->isChecked()) + a->trigger(); + pageAction(QWebPage::InspectElement)->trigger(); +} + + +void WebView::scrollFrameChanged() +{ + // do the scrolling + page()->currentFrame()->scroll(m_horizontalAutoScrollSpeed, m_verticalAutoScrollSpeed); + + // check if we reached the end + int y = page()->currentFrame()->scrollPosition().y(); + if (y == 0 || y == page()->currentFrame()->scrollBarMaximum(Qt::Vertical)) + m_verticalAutoScrollSpeed = 0; + + int x = page()->currentFrame()->scrollPosition().x(); + if (x == 0 || x == page()->currentFrame()->scrollBarMaximum(Qt::Horizontal)) + m_horizontalAutoScrollSpeed = 0; +} + + +void WebView::setupSmoothScrolling(int posY) +{ + int ddy = qMax(m_smoothScrollSteps ? abs(m_dy) / m_smoothScrollSteps : 0, 3); + + m_dy += posY; + + if (m_dy <= 0) + { + stopSmoothScrolling(); + return; + } + + m_smoothScrollSteps = 8; + + if (m_dy / m_smoothScrollSteps < ddy) + { + m_smoothScrollSteps = (abs(m_dy) + ddy - 1) / ddy; + if (m_smoothScrollSteps < 1) + m_smoothScrollSteps = 1; + } + + m_smoothScrollTime.start(); + + if (!m_isViewSmoothScrolling) + { + m_isViewSmoothScrolling = true; + m_smoothScrollTimer->start(); + scrollTick(); + } +} + + +void WebView::scrollTick() +{ + if (m_dy == 0) + { + stopSmoothScrolling(); + return; + } + + if (m_smoothScrollSteps < 1) + m_smoothScrollSteps = 1; + + int takesteps = m_smoothScrollTime.restart() / 16; + int scroll_y = 0; + + if (takesteps < 1) + takesteps = 1; + + if (takesteps > m_smoothScrollSteps) + takesteps = m_smoothScrollSteps; + + for (int i = 0; i < takesteps; i++) + { + int ddy = (m_dy / (m_smoothScrollSteps + 1)) * 2; + + // limit step to requested scrolling distance + if (abs(ddy) > abs(m_dy)) + ddy = m_dy; + + // update remaining scroll + m_dy -= ddy; + scroll_y += ddy; + m_smoothScrollSteps--; + } + + if (m_smoothScrollBottomReached) + page()->currentFrame()->scroll(0, scroll_y); + else + page()->currentFrame()->scroll(0, -scroll_y); +} + + +void WebView::stopSmoothScrolling() +{ + m_smoothScrollTimer->stop(); + m_dy = 0; + m_isViewSmoothScrolling = false; +} + + +void WebView::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasUrls() || event->mimeData()->hasText()) + event->acceptProposedAction(); + else + KWebView::dragEnterEvent(event); +} + + +void WebView::dragMoveEvent(QDragMoveEvent *event) +{ + if (event->mimeData()->hasUrls() || event->mimeData()->hasText()) + event->acceptProposedAction(); + else + KWebView::dragMoveEvent(event); +} + + +void WebView::hideAccessKeys() +{ + if (!m_accessKeyLabels.isEmpty()) + { + for (int i = 0; i < m_accessKeyLabels.count(); ++i) + { + QLabel *label = m_accessKeyLabels[i]; + label->hide(); + label->deleteLater(); + } + m_accessKeyLabels.clear(); + m_accessKeyNodes.clear(); + update(); + } +} + + +void WebView::showAccessKeys() +{ + QStringList supportedElement; + supportedElement << QLatin1String("a") + << QLatin1String("input") + << QLatin1String("area") + << QLatin1String("button") + << QLatin1String("label") + << QLatin1String("legend") + << QLatin1String("textarea"); + + QList<QChar> unusedKeys; + for (char c = 'A'; c <= 'Z'; ++c) + unusedKeys << QLatin1Char(c); + for (char c = '0'; c <= '9'; ++c) + unusedKeys << QLatin1Char(c); + + QRect viewport = QRect(page()->mainFrame()->scrollPosition(), page()->viewportSize()); + // Priority first goes to elements with accesskey attributes + QList<QWebElement> alreadyLabeled; + Q_FOREACH(const QString & elementType, supportedElement) + { + QList<QWebElement> result = page()->mainFrame()->findAllElements(elementType).toList(); + Q_FOREACH(const QWebElement & element, result) + { + const QRect geometry = element.geometry(); + if (geometry.size().isEmpty() + || !viewport.contains(geometry.topLeft())) + { + continue; + } + QString accessKeyAttribute = element.attribute(QLatin1String("accesskey")).toUpper(); + if (accessKeyAttribute.isEmpty()) + continue; + QChar accessKey; + for (int i = 0; i < accessKeyAttribute.count(); i += 2) + { + const QChar &possibleAccessKey = accessKeyAttribute[i]; + if (unusedKeys.contains(possibleAccessKey)) + { + accessKey = possibleAccessKey; + break; + } + } + if (accessKey.isNull()) + { + continue; + } + unusedKeys.removeOne(accessKey); + makeAccessKeyLabel(accessKey, element); + alreadyLabeled.append(element); + } + } + + // Pick an access key first from the letters in the text and then from the + // list of unused access keys + Q_FOREACH(const QString & elementType, supportedElement) + { + QWebElementCollection result = page()->mainFrame()->findAllElements(elementType); + Q_FOREACH(const QWebElement & element, result) + { + const QRect geometry = element.geometry(); + if (unusedKeys.isEmpty() + || alreadyLabeled.contains(element) + || geometry.size().isEmpty() + || !viewport.contains(geometry.topLeft())) + { + continue; + } + QChar accessKey; + QString text = element.toPlainText().toUpper(); + for (int i = 0; i < text.count(); ++i) + { + const QChar &c = text.at(i); + if (unusedKeys.contains(c)) + { + accessKey = c; + break; + } + } + if (accessKey.isNull()) + accessKey = unusedKeys.takeFirst(); + unusedKeys.removeOne(accessKey); + makeAccessKeyLabel(accessKey, element); + } + } +} + + +void WebView::makeAccessKeyLabel(const QChar &accessKey, const QWebElement &element) +{ + QLabel *label = new QLabel(this); + label->setText(QString(QLatin1String("<qt><b>%1</b>")).arg(accessKey)); + + label->setAutoFillBackground(true); + label->setFrameStyle(QFrame::Box | QFrame::Plain); + QPoint point = element.geometry().center(); + point -= page()->mainFrame()->scrollPosition(); + label->move(point); + label->show(); + point.setX(point.x() - label->width() / 2); + label->move(point); + m_accessKeyLabels.append(label); + m_accessKeyNodes[accessKey] = element; +} + + +bool WebView::checkForAccessKey(QKeyEvent *event) +{ + if (m_accessKeyLabels.isEmpty()) + return false; + + QString text = event->text(); + if (text.isEmpty()) + return false; + QChar key = text.at(0).toUpper(); + bool handled = false; + if (m_accessKeyNodes.contains(key)) + { + QWebElement element = m_accessKeyNodes[key]; + QPoint p = element.geometry().center(); + QWebFrame *frame = element.webFrame(); + Q_ASSERT(frame); + do + { + p -= frame->scrollPosition(); + frame = frame->parentFrame(); + } + while (frame && frame != page()->mainFrame()); + QMouseEvent pevent(QEvent::MouseButtonPress, p, Qt::LeftButton, 0, 0); + rApp->sendEvent(this, &pevent); + QMouseEvent revent(QEvent::MouseButtonRelease, p, Qt::LeftButton, 0, 0); + rApp->sendEvent(this, &revent); + handled = true; + } + + kDebug() << "checking for access keys: " << handled; + return handled; +} + + +void WebView::accessKeyShortcut() +{ + if (!hasFocus() + || !m_accessKeysPressed + || !ReKonfig::accessKeysEnabled()) + return; + if (m_accessKeyLabels.isEmpty()) + { + showAccessKeys(); + } + else + { + hideAccessKeys(); + } + m_accessKeysPressed = false; +} + + +void WebView::sendByMail() +{ + KAction *a = qobject_cast<KAction*>(sender()); + QString url = a->data().toString(); + + KToolInvocation::invokeMailer("", "", "", "", url); +} + + +void WebView::blockImage() +{ + QAction *action = qobject_cast<QAction*>(sender()); + if (!action) + return; + + QString imageUrl = action->data().toString(); + rApp->adblockManager()->addCustomRule(imageUrl); +} + + +void WebView::mouseReleaseEvent(QMouseEvent *event) +{ + QWebHitTestResult hitTest = page()->mainFrame()->hitTestContent(event->pos()); + const QUrl url = hitTest.linkUrl(); + + if (!url.isEmpty()) + { + if (event->button() & Qt::MidButton) + { + if (event->modifiers() & Qt::ShiftModifier) + { + if (ReKonfig::openNewTabsInBackground()) + emit loadUrl(url, Rekonq::NewFocusedTab); + else + emit loadUrl(url, Rekonq::NewBackGroundTab); + event->accept(); + return; + } + + emit loadUrl(url, Rekonq::NewTab); + event->accept(); + return; + } + + if ((event->button() & Qt::LeftButton) && (event->modifiers() & Qt::ControlModifier)) + { + emit loadUrl(url, Rekonq::NewTab); + event->accept(); + return; + } + + if ((event->button() & Qt::LeftButton) && (event->modifiers() & Qt::ShiftModifier)) + { + page()->downloadUrl(url); + event->accept(); + return; + } + } + + QWebView::mouseReleaseEvent(event); +} diff --git a/src/webtab/webview.h b/src/webtab/webview.h new file mode 100644 index 00000000..0141925c --- /dev/null +++ b/src/webtab/webview.h @@ -0,0 +1,159 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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/>. +* +* ============================================================ */ + + +#ifndef WEBVIEW_H +#define WEBVIEW_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// KDE Includes +#include <KWebView> + +// Qt Includes +#include <QTime> +#include <QPoint> +#include <QPixmap> + +// Forward Declarations +class WebPage; + +class QLabel; +class QTimer; + + +class REKONQ_TESTS_EXPORT WebView : public KWebView +{ + Q_OBJECT + +public: + + enum ContextType + { + EmptySelection = 0x00000000, + LinkSelection = 0x00000001, + ImageSelection = 0x00000010, + TextSelection = 0x00000100 + }; + + explicit WebView(QWidget *parent); + ~WebView(); + + WebPage *page(); + + void load(const QUrl &url); + void load(const QNetworkRequest &req, + QNetworkAccessManager::Operation op = QNetworkAccessManager::GetOperation, + const QByteArray & body = QByteArray()); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + + void wheelEvent(QWheelEvent *event); + + void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent * event); + + void paintEvent(QPaintEvent *event); + +private Q_SLOTS: + void search(); + + void printFrame(); + + void openLinkInNewWindow(); + void openLinkInNewTab(); + void bookmarkLink(); + void sendByMail(); + + void viewImage(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers); + void slotCopyImageLocation(); + void inspect(); + + void scrollFrameChanged(); + void scrollTick(); + + void setupSmoothScrolling(int posY); + + void stopSmoothScrolling(); + void changeWindowIcon(); + + void accessKeyShortcut(); + void hideAccessKeys(); + + void loadStarted(); + + void blockImage(); + + +Q_SIGNALS: + void loadUrl(const KUrl &, const Rekonq::OpenType &); + void zoomChanged(int); + void openPreviousInHistory(); + void openNextInHistory(); + +private: + bool checkForAccessKey(QKeyEvent *event); + void showAccessKeys(); + void makeAccessKeyLabel(const QChar &accessKey, const QWebElement &element); + +private: + WebPage *m_page; + QPoint m_clickPos; + + // Auto Scroll + QTimer *const m_autoScrollTimer; + int m_verticalAutoScrollSpeed; + int m_horizontalAutoScrollSpeed; + bool m_isViewAutoScrolling; + QPixmap m_autoScrollIndicator; + + // Smooth Scroll + QTimer *const m_smoothScrollTimer; + QTime m_smoothScrollTime; + bool m_smoothScrollBottomReached; + int m_dy; + int m_smoothScrollSteps; + bool m_isViewSmoothScrolling; + + // Access Keys + QList<QLabel*> m_accessKeyLabels; + QHash<QChar, QWebElement> m_accessKeyNodes; + bool m_accessKeysPressed; + bool m_accessKeysActive; +}; + +#endif |