diff options
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 |