Unbreak favicon checking; in many error cases it wouldn't proceed to the next item.

Also when the cached favicon was recent enough, then the kded module wouldn't emit anything;
fixed by adding a "forceDownloadHostIcon" and an error signal in the favicons kded module.
CCBUG: 23102
 (and probably others)

svn path=/trunk/KDE/kdebase/apps/; revision=1116050
This commit is contained in:
David Faure 2010-04-18 14:24:11 +00:00
parent 1cd5acff40
commit 7f5e80a8d3
14 changed files with 336 additions and 250 deletions

View file

@ -68,6 +68,11 @@ void KEBApp::createActions() {
m_actionsImpl = new ActionsImpl(this, GlobalBookmarkManager::self()->model());
connect(m_actionsImpl->testLinkHolder(), SIGNAL(setCancelEnabled(bool)),
this, SLOT(setCancelTestsEnabled(bool)));
connect(m_actionsImpl->favIconHolder(), SIGNAL(setCancelEnabled(bool)),
this, SLOT(setCancelFavIconUpdatesEnabled(bool)));
// save and quit should probably not be in the toplevel???
(void) KStandardAction::quit(
this, SLOT( close() ), actionCollection());
@ -438,12 +443,12 @@ void ActionsImpl::slotCancelAllTests() {
}
void ActionsImpl::slotTestAll() {
m_testLinkHolder->insertItr(
m_testLinkHolder->insertIterator(
new TestLinkItr(m_testLinkHolder, KEBApp::self()->allBookmarks()));
}
void ActionsImpl::slotUpdateAllFavIcons() {
m_favIconHolder->insertItr(
m_favIconHolder->insertIterator(
new FavIconsItr(m_favIconHolder, KEBApp::self()->allBookmarks()));
}
@ -456,12 +461,12 @@ ActionsImpl::~ActionsImpl() {
void ActionsImpl::slotTestSelection() {
KEBApp::self()->bkInfo()->commitChanges();
m_testLinkHolder->insertItr(new TestLinkItr(m_testLinkHolder, KEBApp::self()->selectedBookmarksExpanded()));
m_testLinkHolder->insertIterator(new TestLinkItr(m_testLinkHolder, KEBApp::self()->selectedBookmarksExpanded()));
}
void ActionsImpl::slotUpdateFavIcon() {
KEBApp::self()->bkInfo()->commitChanges();
m_favIconHolder->insertItr(new FavIconsItr(m_favIconHolder, KEBApp::self()->selectedBookmarksExpanded()));
m_favIconHolder->insertIterator(new FavIconsItr(m_favIconHolder, KEBApp::self()->selectedBookmarksExpanded()));
}
/* -------------------------------------- */

View file

@ -36,6 +36,9 @@ public:
~ActionsImpl();
bool save();
TestLinkItrHolder* testLinkHolder() { return m_testLinkHolder; }
FavIconsItrHolder* favIconHolder() { return m_favIconHolder; }
public Q_SLOTS:
void slotLoad();
void slotSaveAs();

View file

@ -1,7 +1,5 @@
// -*- indent-tabs-mode:nil -*-
// vim: set ts=4 sts=4 sw=4 et:
/* This file is part of the KDE project
Copyright (C) 2000 David Faure <faure@kde.org>
Copyright (C) 2000, 2010 David Faure <faure@kde.org>
Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
This program is free software; you can redistribute it and/or
@ -21,15 +19,14 @@
*/
#include "bookmarkiterator.h"
#include "toplevel.h"
#include "bookmarkmodel.h"
#include <kbookmarkmanager.h>
#include <kdebug.h>
#include <QtCore/QTimer>
#include <assert.h>
BookmarkIterator::BookmarkIterator(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks)
: m_bklist(bks), m_holder(holder)
: m_bookmarkList(bks), m_holder(holder)
{
delayedEmitNextOne();
}
@ -43,7 +40,7 @@ void BookmarkIterator::delayedEmitNextOne()
QTimer::singleShot(1, this, SLOT(nextOne()));
}
KBookmark BookmarkIterator::curBk()
KBookmark BookmarkIterator::currentBookmark()
{
return m_bk;
}
@ -52,25 +49,25 @@ void BookmarkIterator::nextOne()
{
// kDebug() << "BookmarkIterator::nextOne";
if (m_bklist.isEmpty()) {
holder()->removeItr(this); // deletes "this"
// Look for an interesting bookmark
while (!m_bookmarkList.isEmpty()) {
KBookmark bk = m_bookmarkList.takeFirst();
if (bk.hasParent() && isApplicable(bk)) {
m_bk = bk;
doAction();
// Async action started, we'll have to come back later
return;
}
}
if (m_bookmarkList.isEmpty()) {
holder()->removeIterator(this); // deletes "this"
return;
}
}
QList<KBookmark>::iterator head = m_bklist.begin();
KBookmark bk = (*head);
bool viable = bk.hasParent() && isApplicable(bk);
if (viable) {
m_bk = bk;
doAction();
}
m_bklist.erase(head);
if (!viable)
delayedEmitNextOne();
KBookmarkModel* BookmarkIterator::model()
{
return m_holder->model();
}
/* --------------------------- */
@ -81,29 +78,49 @@ BookmarkIteratorHolder::BookmarkIteratorHolder(KBookmarkModel* model)
Q_ASSERT(m_model);
}
void BookmarkIteratorHolder::insertItr(BookmarkIterator *itr)
void BookmarkIteratorHolder::insertIterator(BookmarkIterator *itr)
{
m_itrs.prepend(itr);
doItrListChanged();
m_iterators.prepend(itr);
doIteratorListChanged();
}
void BookmarkIteratorHolder::removeItr(BookmarkIterator *itr)
void BookmarkIteratorHolder::removeIterator(BookmarkIterator *itr)
{
m_itrs.removeAll(itr);
m_iterators.removeAll(itr);
itr->deleteLater();
doItrListChanged();
doIteratorListChanged();
}
void BookmarkIteratorHolder::cancelAllItrs()
{
qDeleteAll(m_itrs);
m_itrs.clear();
doItrListChanged();
Q_FOREACH(BookmarkIterator* iterator, m_iterators) {
iterator->cancel();
}
qDeleteAll(m_iterators);
m_iterators.clear();
doIteratorListChanged();
}
KBookmarkModel* BookmarkIterator::model()
void BookmarkIteratorHolder::addAffectedBookmark(const QString & address)
{
return m_holder->model();
kDebug() << address;
if(m_affectedBookmark.isNull())
m_affectedBookmark = address;
else
m_affectedBookmark = KBookmark::commonParent(m_affectedBookmark, address);
kDebug() << "m_affectedBookmark is now" << m_affectedBookmark;
}
void BookmarkIteratorHolder::doIteratorListChanged()
{
kDebug() << count() << "iterators";
emit setCancelEnabled(count() > 0);
if(count() == 0) {
kDebug() << "Notifing managers" << m_affectedBookmark;
KBookmarkManager* mgr = m_model->bookmarkManager();
model()->notifyManagers(mgr->findByAddress(m_affectedBookmark).toGroup());
m_affectedBookmark.clear();
}
}
#include "bookmarkiterator.moc"

View file

@ -1,5 +1,5 @@
// vim: set ts=4 sts=4 sw=4 et:
/* This file is part of the KDE project
Copyright (C) 2010 David Faure <faure@kde.org>
Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
This program is free software; you can redistribute it and/or
@ -26,47 +26,65 @@
class KBookmarkModel;
class BookmarkIteratorHolder;
/**
* A bookmark iterator goes through every bookmark and performs an asynchronous
* action (e.g. downloading the favicon or testing whether the url exists).
*/
class BookmarkIterator : public QObject
{
Q_OBJECT
Q_OBJECT
public:
BookmarkIterator(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks);
virtual ~BookmarkIterator();
BookmarkIteratorHolder* holder() const { return m_holder; }
KBookmarkModel* model();
void delayedEmitNextOne();
virtual void cancel() = 0;
public Q_SLOTS:
void nextOne();
void delayedEmitNextOne();
void nextOne();
protected:
virtual void doAction() = 0;
virtual bool isApplicable(const KBookmark &bk) const = 0;
KBookmark curBk();
virtual void doAction() = 0;
virtual bool isApplicable(const KBookmark &bk) const = 0;
KBookmark currentBookmark();
private:
KBookmark m_bk;
QList<KBookmark> m_bklist;
BookmarkIteratorHolder* m_holder;
KBookmark m_bk;
QList<KBookmark> m_bookmarkList;
BookmarkIteratorHolder* m_holder;
};
class BookmarkIteratorHolder
/**
* The "bookmark iterator holder" handles all concurrent iterators for a given
* functionality: e.g. all favicon iterators.
*
* BookmarkIteratorHolder is the base class for the favicon and testlink holders.
*/
class BookmarkIteratorHolder : public QObject
{
Q_OBJECT
public:
void cancelAllItrs();
void removeItr(BookmarkIterator*);
void insertItr(BookmarkIterator*);
virtual void addAffectedBookmark(const QString & address) = 0;
KBookmarkModel* model() { return m_model; }
void cancelAllItrs();
void removeIterator(BookmarkIterator*);
void insertIterator(BookmarkIterator*);
void addAffectedBookmark(const QString & address);
KBookmarkModel* model() { return m_model; }
Q_SIGNALS:
void setCancelEnabled(bool canCancel);
protected:
BookmarkIteratorHolder(KBookmarkModel* model);
virtual ~BookmarkIteratorHolder() {}
virtual void doItrListChanged() = 0;
int count() const { return m_itrs.count(); }
KBookmarkModel* m_model;
BookmarkIteratorHolder(KBookmarkModel* model);
virtual ~BookmarkIteratorHolder() {}
void doIteratorListChanged();
int count() const { return m_iterators.count(); }
KBookmarkModel* m_model;
private:
QList<BookmarkIterator *> m_itrs;
QString m_affectedBookmark;
QList<BookmarkIterator *> m_iterators;
};
#endif

View file

@ -1,5 +1,3 @@
// -*- indent-tabs-mode:nil -*-
// vim: set ts=4 sts=4 sw=4 et:
/* This file is part of the KDE project
Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
@ -22,12 +20,10 @@
#include "favicons.h"
#include "bookmarkiterator.h"
#include "toplevel.h" // for KEBApp
#include "updater.h"
#include "commands.h"
#include "bookmarkmodel.h"
#include <kbookmarkmanager.h>
#include <kdebug.h>
#include <klocale.h>
@ -37,28 +33,6 @@ FavIconsItrHolder::FavIconsItrHolder(KBookmarkModel* model)
// do stuff
}
void FavIconsItrHolder::doItrListChanged() {
kDebug()<<"FavIconsItrHolder::doItrListChanged() "<<count()<<" iterators";
KEBApp::self()->setCancelFavIconUpdatesEnabled(count() > 0);
if(count() == 0)
{
kDebug()<<"Notifing managers "<<m_affectedBookmark;
KBookmarkManager* mgr = m_model->bookmarkManager();
model()->notifyManagers(mgr->findByAddress(m_affectedBookmark).toGroup());
m_affectedBookmark.clear();
}
}
void FavIconsItrHolder::addAffectedBookmark( const QString & address )
{
kDebug()<<"addAffectedBookmark "<<address;
if(m_affectedBookmark.isNull())
m_affectedBookmark = address;
else
m_affectedBookmark = KBookmark::commonParent(m_affectedBookmark, address);
kDebug()<<" m_affectedBookmark is now "<<m_affectedBookmark;
}
/* -------------------------- */
FavIconsItr::FavIconsItr(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks)
@ -68,45 +42,46 @@ FavIconsItr::FavIconsItr(BookmarkIteratorHolder* holder, const QList<KBookmark>&
FavIconsItr::~FavIconsItr()
{
setStatus(m_oldStatus);
delete m_updater;
}
void FavIconsItr::setStatus(const QString & status)
{
curBk().setMetaDataItem("favstate", status);
model()->emitDataChanged(curBk());
currentBookmark().setMetaDataItem("favstate", status);
model()->emitDataChanged(currentBookmark());
}
void FavIconsItr::slotDone(bool succeeded)
void FavIconsItr::slotDone(bool succeeded, const QString& errorString)
{
// kDebug() << "FavIconsItr::slotDone()";
setStatus(succeeded ? i18n("OK") : i18n("No favicon found"));
holder()->addAffectedBookmark(KBookmark::parentAddress(curBk().address()));
setStatus(succeeded ? i18n("OK") : errorString);
holder()->addAffectedBookmark(KBookmark::parentAddress(currentBookmark().address()));
delayedEmitNextOne();
}
bool FavIconsItr::isApplicable(const KBookmark &bk) const
{
return (!bk.isGroup() && !bk.isSeparator());
if (bk.isGroup() || bk.isSeparator())
return false;
return bk.url().protocol().startsWith("http");
}
void FavIconsItr::doAction()
{
// kDebug() << "FavIconsItr::doAction()";
//FIXME ensure that this gets overwritten
m_oldStatus = currentBookmark().metaDataItem("favstate");
setStatus(i18n("Updating favicon..."));
if (!m_updater) {
m_updater = new FavIconUpdater(qApp);
connect(m_updater, SIGNAL( done(bool) ),
this, SLOT( slotDone(bool) ) );
}
if (curBk().url().protocol().startsWith("http")) {
m_updater->downloadIcon(curBk());
} else {
setStatus(i18n("Local file"));
delayedEmitNextOne();
m_updater = new FavIconUpdater(this);
connect(m_updater, SIGNAL(done(bool,QString)),
this, SLOT(slotDone(bool,QString)) );
}
m_updater->downloadIcon(currentBookmark());
}
void FavIconsItr::cancel()
{
setStatus(m_oldStatus);
}
#include "favicons.moc"

View file

@ -27,11 +27,6 @@
class FavIconsItrHolder : public BookmarkIteratorHolder {
public:
FavIconsItrHolder(KBookmarkModel* model);
void addAffectedBookmark( const QString & address );
protected:
virtual void doItrListChanged();
private:
QString m_affectedBookmark;
};
class KBookmarkModel;
@ -45,8 +40,10 @@ public:
FavIconsItr(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks);
~FavIconsItr();
virtual void cancel();
public Q_SLOTS:
void slotDone(bool succeeded);
void slotDone(bool succeeded, const QString& errorString);
protected:
virtual void doAction();

View file

@ -1,5 +1,5 @@
/* This file is part of the KDE project
Copyright (C) 2000 David Faure <faure@kde.org>
Copyright (C) 2000, 2010 David Faure <faure@kde.org>
Copyright (C) 2002-2003 Alexander Kellett <lypanov@kde.org>
This program is free software; you can redistribute it and/or
@ -21,50 +21,18 @@
// Own
#include "testlink.h"
// Qt
#include <QtCore/QTimer>
#include <QtGui/QPainter>
// KDE
#include <kdebug.h>
#include <kdatetime.h>
#include <kcharsets.h>
#include <kbookmarkmanager.h>
#include <kaction.h>
#include <klocale.h>
// Local
#include "toplevel.h" // for KEBApp
#include "commands.h"
#include "bookmarkiterator.h"
#include "bookmarkmodel.h"
TestLinkItrHolder::TestLinkItrHolder(KBookmarkModel* model)
: BookmarkIteratorHolder(model) {
// do stuff
}
void TestLinkItrHolder::doItrListChanged() {
KEBApp::self()->setCancelTestsEnabled(count() > 0);
if(count() == 0)
{
kDebug()<<"Notifing managers "<<m_affectedBookmark;
KBookmarkManager* mgr = m_model->bookmarkManager();
m_model->notifyManagers(mgr->findByAddress(m_affectedBookmark).toGroup());
m_affectedBookmark.clear();
}
}
void TestLinkItrHolder::addAffectedBookmark(const QString & address)
: BookmarkIteratorHolder(model)
{
kDebug() << address;
if(m_affectedBookmark.isNull())
m_affectedBookmark = address;
else
m_affectedBookmark = KBookmark::commonParent(m_affectedBookmark, address);
kDebug() << "m_affectedBookmark is now" << m_affectedBookmark;
}
/* -------------------------- */
@ -74,8 +42,8 @@ TestLinkItr::TestLinkItr(BookmarkIteratorHolder* holder, const QList<KBookmark>&
{
}
TestLinkItr::~TestLinkItr() {
//FIXME setStatus(m_oldStatus); if we didn't finish
TestLinkItr::~TestLinkItr()
{
if (m_job) {
// kDebug() << "JOB kill\n";
m_job->disconnect(this);
@ -85,52 +53,57 @@ TestLinkItr::~TestLinkItr() {
void TestLinkItr::setStatus(const QString & text)
{
curBk().setMetaDataItem("linkstate", text);
model()->emitDataChanged(curBk());
currentBookmark().setMetaDataItem("linkstate", text);
model()->emitDataChanged(currentBookmark());
}
bool TestLinkItr::isApplicable(const KBookmark &bk) const {
return (!bk.isGroup() && !bk.isSeparator());
bool TestLinkItr::isApplicable(const KBookmark &bk) const
{
return !bk.isGroup() && !bk.isSeparator();
}
void TestLinkItr::doAction() {
void TestLinkItr::doAction()
{
kDebug();
m_job = KIO::get(curBk().url(), KIO::Reload, KIO::HideProgressInfo);
m_job = KIO::get(currentBookmark().url(), KIO::Reload, KIO::HideProgressInfo);
m_job->addMetaData( QString("cookies"), QString("none") );
m_job->addMetaData( QString("errorPage"), QString("false") );
connect(m_job, SIGNAL( result( KJob *)),
this, SLOT( slotJobResult(KJob *)));
m_oldStatus = curBk().metaDataItem("linkstate");
m_oldStatus = currentBookmark().metaDataItem("linkstate");
setStatus(i18n("Checking..."));
}
void TestLinkItr::slotJobResult(KJob *job) {
void TestLinkItr::slotJobResult(KJob *job)
{
kDebug();
m_job = 0;
KIO::TransferJob *transfer = (KIO::TransferJob *)job;
QString modDate = transfer->queryMetaData("modified");
KIO::TransferJob *transfer = static_cast<KIO::TransferJob *>(job);
const QString modDate = transfer->queryMetaData("modified");
if (transfer->error() || transfer->isErrorPage())
{
if (transfer->error() || transfer->isErrorPage()) {
kDebug()<<"***********"<<transfer->error()<<" "<<transfer->isErrorPage()<<endl;
// can we assume that errorString will contain no entities?
QString err = transfer->errorString();
err.replace("\n", " ");
setStatus(err);
}
else
{
} else {
if (!modDate.isEmpty())
setStatus(modDate);
else
setStatus(i18n("OK"));
}
holder()->addAffectedBookmark(KBookmark::parentAddress(curBk().address()));
holder()->addAffectedBookmark(KBookmark::parentAddress(currentBookmark().address()));
delayedEmitNextOne();
//FIXME check that we don't need to call kill()
}
void TestLinkItr::cancel()
{
setStatus(m_oldStatus);
}
#include "testlink.moc"

View file

@ -30,11 +30,6 @@ class KBookmarkModel;
class TestLinkItrHolder : public BookmarkIteratorHolder {
public:
TestLinkItrHolder(KBookmarkModel* model);
void addAffectedBookmark(const QString & address);
protected:
virtual void doItrListChanged();
private:
QString m_affectedBookmark;
};
class TestLinkItr : public BookmarkIterator
@ -45,6 +40,8 @@ public:
TestLinkItr(BookmarkIteratorHolder* holder, const QList<KBookmark>& bks);
~TestLinkItr();
virtual void cancel();
public Q_SLOTS:
void slotJobResult(KJob *job);

View file

@ -62,9 +62,6 @@ public:
SelcAbilities getSelectionAbilities() const;
void setActionsEnabled(SelcAbilities);
void setCancelFavIconUpdatesEnabled(bool);
void setCancelTestsEnabled(bool);
QMenu* popupMenuFactory(const char *type)
{
QWidget * menu = factory()->container(type, this);
@ -104,10 +101,12 @@ public Q_SLOTS:
public Q_SLOTS:
void slotConfigureToolbars();
protected Q_SLOTS:
private Q_SLOTS:
void slotClipboardDataChanged();
void slotNewToolbarConfig();
void selectionChanged();
void setCancelFavIconUpdatesEnabled(bool);
void setCancelTestsEnabled(bool);
private:
void selectedBookmarksExpandedHelper(const KBookmark& bk,

View file

@ -32,112 +32,130 @@
#include <kmimetype.h>
#include <kparts/part.h>
#include <kparts/browserextension.h>
#include <kservicetypetrader.h>
#include <kmimetypetrader.h>
FavIconUpdater::FavIconUpdater(QObject *parent)
: QObject(parent),
m_favIconModule("org.kde.kded", "/modules/favicons", QDBusConnection::sessionBus())
{
QObject::connect(&m_favIconModule, SIGNAL(iconChanged(bool,QString,QString)),
this, SLOT(notifyChange(bool,QString,QString)) );
connect(&m_favIconModule, SIGNAL(iconChanged(bool,QString,QString)),
this, SLOT(notifyChange(bool,QString,QString)) );
connect(&m_favIconModule, SIGNAL(error(bool,QString,QString)),
this, SLOT(slotFavIconError(bool,QString,QString)) );
m_part = 0;
m_webGrabber = 0;
m_browserIface = 0;
}
void FavIconUpdater::slotCompleted() {
// kDebug() << "FavIconUpdater::slotCompleted";
// kDebug() << "emit done(true)";
emit done(true);
}
void FavIconUpdater::downloadIcon(const KBookmark &bk) {
void FavIconUpdater::downloadIcon(const KBookmark &bk)
{
m_bk = bk;
const QString & url = bk.url().url();
QString favicon = KMimeType::favIconForUrl(url);
if (!favicon.isNull()) {
// kDebug() << "downloadIcon() - favicon" << favicon;
const QString url = bk.url().url();
const QString favicon = KMimeType::favIconForUrl(url);
if (!favicon.isEmpty()) {
kDebug() << "got favicon" << favicon;
bk.internalElement().setAttribute("icon", favicon);
KEBApp::self()->notifyCommandExecuted();
// kDebug() << "emit done(true)";
emit done(true);
emit done(true, QString());
} else {
kDebug()<<"no favicon found "<<endl;
kDebug() << "no favicon found";
webupdate = false;
m_favIconModule.downloadHostIcon(url);
m_favIconModule.forceDownloadHostIcon(url);
}
}
FavIconUpdater::~FavIconUpdater() {
// kDebug() << "~FavIconUpdater";
FavIconUpdater::~FavIconUpdater()
{
delete m_browserIface;
delete m_webGrabber;
delete m_part;
}
void FavIconUpdater::downloadIconActual(const KBookmark &bk) {
kDebug()<<"FavIconUpdater::downloadIconActual"<<endl;
void FavIconUpdater::downloadIconUsingWebBrowser(const KBookmark &bk, const QString& currentError)
{
kDebug();
m_bk = bk;
webupdate = true;
if (!m_part) {
QString partLoadingError;
KParts::ReadOnlyPart *part
= KServiceTypeTrader
::createInstanceFromQuery<KParts::ReadOnlyPart>("text/html", QString());
= KMimeTypeTrader::createPartInstanceFromQuery<KParts::ReadOnlyPart>("text/html", 0, this, QString(), QVariantList(), &partLoadingError);
if (!part) {
emit done(false, i18n("%1; no HTML component found (%2)", currentError, partLoadingError));
return;
}
part->setProperty("pluginsEnabled", QVariant(false));
part->setProperty("javaScriptEnabled", QVariant(false));
part->setProperty("javaEnabled", QVariant(false));
part->setProperty("autoloadImages", QVariant(false));
//FIXME only connect to result?
// connect(part, SIGNAL( result(KIO::Job * job)),
// this, SLOT( slotCompleted()));
connect(part, SIGNAL( canceled(const QString &) ),
this, SLOT( slotCompleted() ));
connect(part, SIGNAL( completed() ),
this, SLOT( slotCompleted() ));
KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(part);
Q_ASSERT(ext);
// TODO: what is this useful for?
m_browserIface = new FavIconBrowserInterface(this);
ext->setBrowserInterface(m_browserIface);
connect(ext, SIGNAL( setIconURL(const KUrl &) ),
this, SLOT( setIconURL(const KUrl &) ));
connect(ext, SIGNAL(setIconUrl(KUrl)),
this, SLOT(setIconUrl(KUrl)));
m_part = part;
}
// The part isn't created by the webgrabber so that we can create the part
// only once.
delete m_webGrabber;
m_webGrabber = new FavIconWebGrabber(m_part, bk.url());
connect(m_webGrabber, SIGNAL(done(bool,QString)), this, SIGNAL(done(bool,QString)));
}
// khtml callback
void FavIconUpdater::setIconURL(const KUrl &iconURL) {
void FavIconUpdater::setIconUrl(const KUrl &iconURL)
{
m_favIconModule.setIconForUrl(m_bk.url().url(), iconURL.url());
// The above call will make the kded module start the download and emit iconChanged or error.
delete m_webGrabber;
m_webGrabber = 0;
}
bool FavIconUpdater::isFavIconSignalRelevant(bool isHost, const QString& hostOrURL) const
{
// Is this signal interesting to us? (Don't react on an unrelated favicon)
return (isHost && hostOrURL == m_bk.url().host()) ||
(!isHost && hostOrURL == m_bk.url().url()); // should we use the api that ignores trailing slashes?
}
void FavIconUpdater::notifyChange(bool isHost,
const QString& hostOrURL,
const QString& iconName)
{
Q_UNUSED(isHost);
Q_UNUSED(hostOrURL);
// kDebug() << "FavIconUpdater::notifyChange()";
if(iconName.isNull() && !webupdate)
{
// no icon found, try webupdater
downloadIconActual(m_bk);
kDebug() << hostOrURL << iconName;
if (isFavIconSignalRelevant(isHost, hostOrURL)) {
if (iconName.isEmpty()) { // old version of the kded module could emit with an empty iconName on error
slotFavIconError(isHost, hostOrURL, QString());
} else {
m_bk.internalElement().setAttribute("icon", iconName);
emit done(true, QString());
}
}
else
{
// Either we have an icon or we already tried the webupdater
m_bk.internalElement().setAttribute("icon", iconName);
emit done(!iconName.isNull());
}
void FavIconUpdater::slotFavIconError(bool isHost, const QString& hostOrURL, const QString& errorString)
{
kDebug() << hostOrURL << errorString;
if (isFavIconSignalRelevant(isHost, hostOrURL)) {
if (!webupdate) {
// no icon found, try webupdater
downloadIconUsingWebBrowser(m_bk, errorString);
} else {
// already tried webupdater
emit done(false, errorString);
}
}
}
@ -146,10 +164,18 @@ void FavIconUpdater::notifyChange(bool isHost,
FavIconWebGrabber::FavIconWebGrabber(KParts::ReadOnlyPart *part, const KUrl &url)
: m_part(part), m_url(url) {
// kDebug() << "FavIconWebGrabber::FavIconWebGrabber starting KIO::get()";
//FIXME only connect to result?
// connect(part, SIGNAL( result(KIO::Job * job)),
// this, SLOT( slotCompleted()));
connect(part, SIGNAL(canceled(QString)),
this, SLOT(slotCanceled(QString)));
connect(part, SIGNAL(completed()),
this, SLOT(slotCompleted()));
// the use of KIO rather than directly using KHTML is to allow silently abort on error
// TODO: an alternative would be to derive from KHTMLPart and reimplement showError(KJob*).
kDebug() << "starting KIO::get() on" << m_url;
KIO::Job *job = KIO::get(m_url, KIO::NoReload, KIO::HideProgressInfo);
job->addMetaData( QString("cookies"), QString("none") );
connect(job, SIGNAL( result( KJob *)),
@ -170,10 +196,25 @@ void FavIconWebGrabber::slotMimetype(KIO::Job *job, const QString & /*type*/) {
m_part->openUrl(m_url);
}
void FavIconWebGrabber::slotFinished(KJob *job) {
void FavIconWebGrabber::slotFinished(KJob *job)
{
if (job->error()) {
// kDebug() << "FavIconWebGrabber::slotFinished() " << job->errorString();
kDebug() << job->errorString();
emit done(false, job->errorString());
}
// On success mimetype was emitted, so no need to do anything.
}
void FavIconWebGrabber::slotCompleted()
{
kDebug();
emit done(true, QString());
}
void FavIconWebGrabber::slotCanceled(const QString& errorString)
{
kDebug() << errorString;
emit done(false, errorString);
}
#include "updater.moc"

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 4; indent-tabs-mode:nil -*-
// vim: set ts=4 sts=4 sw=4 et:
/* This file is part of the KDE project
Copyright (C) 2003 Alexander Kellett <lypanov@kde.org>
@ -33,9 +31,14 @@ public:
FavIconWebGrabber(KParts::ReadOnlyPart *part, const KUrl &url);
~FavIconWebGrabber() {}
protected Q_SLOTS:
Q_SIGNALS:
void done(bool succeeded, const QString& errorString);
private Q_SLOTS:
void slotMimetype(KIO::Job *job, const QString &_type);
void slotFinished(KJob *job);
void slotCanceled(const QString& errorString);
void slotCompleted();
private:
KParts::ReadOnlyPart *m_part;
@ -52,15 +55,18 @@ public:
FavIconUpdater(QObject *parent);
~FavIconUpdater();
void downloadIcon(const KBookmark &bk);
void downloadIconActual(const KBookmark &bk);
void downloadIconUsingWebBrowser(const KBookmark &bk, const QString& currentError);
private Q_SLOTS:
void setIconURL(const KUrl &iconURL);
void slotCompleted();
void setIconUrl(const KUrl &iconURL);
void notifyChange(bool isHost, const QString& hostOrURL, const QString& iconName);
void slotFavIconError(bool isHost, const QString& hostOrURL, const QString& errorString);
Q_SIGNALS:
void done(bool succeeded);
void done(bool succeeded, const QString& error);
private:
bool isFavIconSignalRelevant(bool isHost, const QString& hostOrURL) const;
private:
KParts::ReadOnlyPart *m_part;

View file

@ -18,6 +18,7 @@
*/
#include "favicons.h"
#include <klocale.h>
#include "favicons_adaptor.h"
#include <time.h>
@ -93,7 +94,7 @@ struct FavIconsModulePrivate
}
QMap<KJob *, DownloadInfo> downloads;
QStringList failedDownloads;
KUrl::List failedDownloads;
KConfig *config;
QList<KIO::Job*> killJobs;
KIO::MetaData metaData;
@ -141,6 +142,8 @@ QString FavIconsModule::iconForUrl(const KUrl &url)
if (url.host().isEmpty())
return QString();
//kDebug() << url;
QString icon;
QString simplifiedURL = simplifyURL(url);
@ -166,14 +169,18 @@ QString FavIconsModule::iconForUrl(const KUrl &url)
bool FavIconsModule::isIconOld(const QString &icon)
{
struct stat st;
if (stat(QFile::encodeName(icon), &st) != 0)
if (stat(QFile::encodeName(icon), &st) != 0) {
//kDebug() << "isIconOld" << icon << "yes, no such file";
return true; // Trigger a new download on error
}
//kDebug() << "isIconOld" << icon << "?";
return (time(0) - st.st_mtime) > 604800; // arbitrary value (one week)
}
void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL)
{
//kDebug() << url << iconURL;
const QString simplifiedURL = simplifyURL(url);
d->faviconsCache.insert(removeSlash(simplifiedURL), new QString(iconURL.url()) );
@ -182,6 +189,7 @@ void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL)
const QString iconFile = d->faviconsDir + iconName + ".png";
if (!isIconOld(iconFile)) {
//kDebug() << "emit iconChanged" << false << url << iconName;
emit iconChanged(false, url.url(), iconName);
return;
}
@ -191,19 +199,32 @@ void FavIconsModule::setIconForUrl(const KUrl &url, const KUrl &iconURL)
void FavIconsModule::downloadHostIcon(const KUrl &url)
{
//kDebug() << url;
const QString iconFile = d->faviconsDir + "favicons/" + url.host() + ".png";
if (!isIconOld(iconFile))
if (!isIconOld(iconFile)) {
//kDebug() << "not old -> doing nothing";
return;
}
startDownload(url.host(), true, KUrl(url, "/favicon.ico"));
}
void FavIconsModule::forceDownloadHostIcon(const KUrl &url)
{
//kDebug() << url;
KUrl iconURL = KUrl(url, "/favicon.ico");
d->failedDownloads.removeAll(iconURL); // force a download to happen
startDownload(url.host(), true, iconURL);
}
void FavIconsModule::startDownload(const QString &hostOrURL, bool isHost, const KUrl &iconURL)
{
if (d->failedDownloads.contains(iconURL.url())) {
if (d->failedDownloads.contains(iconURL)) {
//kDebug() << iconURL << "already in failedDownloads, emitting error";
emit error(isHost, hostOrURL, i18n("No favicon found"));
return;
}
//kDebug() << iconURL;
KIO::Job *job = KIO::get(iconURL, KIO::NoReload, KIO::HideProgressInfo);
job->addMetaData(d->metaData);
job->addMetaData("errorPage", "false");
@ -228,7 +249,7 @@ void FavIconsModule::slotData(KIO::Job *job, const QByteArray &data)
d->killJobs.append(job);
QTimer::singleShot(0, this, SLOT(slotKill()));
const KUrl iconURL = tjob->url();
d->failedDownloads.append(iconURL.url());
d->failedDownloads.append(iconURL);
}
download.iconData.resize(oldSize + data.size());
memcpy(download.iconData.data() + oldSize, data.data(), data.size());
@ -242,6 +263,7 @@ void FavIconsModule::slotResult(KJob *job)
d->downloads.remove(job);
const KUrl iconURL = tjob->url();
QString iconName;
QString errorMessage;
if (!job->error())
{
QBuffer buffer(&download.iconData);
@ -257,20 +279,28 @@ void FavIconsModule::slotResult(KJob *job)
}
}
ir.setScaledSize( desired );
QImage img = ir.read();
const QImage img = ir.read();
if( !img.isNull() ) {
iconName = d->makeIconName(download, iconURL);
if( !img.save( d->faviconsDir + iconName + ".png", "PNG" ) )
const QString localPath = d->faviconsDir + iconName + ".png";
if( !img.save(localPath, "PNG") ) {
iconName.clear();
else if (!download.isHost)
errorMessage = i18n("Error saving image to %1", localPath);
} else if (!download.isHost)
d->config->group(QString()).writeEntry( removeSlash(download.hostOrURL), iconURL.url());
}
}
} else {
errorMessage = job->errorString();
}
if (iconName.isEmpty()) {
//kDebug() << "adding" << iconURL << "to failed downloads";
d->failedDownloads.append(iconURL);
emit error(download.isHost, download.hostOrURL, errorMessage);
} else {
//kDebug() << "emit iconChanged" << download.isHost << download.hostOrURL << iconName;
emit iconChanged(download.isHost, download.hostOrURL, iconName);
}
if (iconName.isEmpty())
d->failedDownloads.append(iconURL.url());
emit iconChanged(download.isHost, download.hostOrURL, iconName);
}
void FavIconsModule::slotInfoMessage(KJob *job, const QString &msg)
@ -280,11 +310,10 @@ void FavIconsModule::slotInfoMessage(KJob *job, const QString &msg)
void FavIconsModule::slotKill()
{
//kDebug();
Q_FOREACH(KIO::Job* job, d->killJobs)
job->kill();
d->killJobs.clear();
}
#include "favicons.moc"
// vim: ts=4 sw=4 et

View file

@ -31,7 +31,7 @@ namespace KIO { class Job; }
* URLs and hosts with shortcut icons and the icons' downloads in a central
* place.
*
* After a successful download, the DBUS signal iconChanged() is emitted.
* After a successful download, the D-Bus signal iconChanged() is emitted.
* It has the signature void iconChanged(bool, QString, QString);
* The first parameter is true if the icon is a "host" icon, that is it is
* the default icon for all URLs on the given host. In this case, the
@ -67,7 +67,7 @@ public Q_SLOTS: // dbus methods, called by the adaptor
/**
* Associates an icon with the given URL. If the icon was not
* downloaded before or the downloaded was too long ago, a
* download attempt will be started and the iconChanged() DBUS
* download attempt will be started and the iconChanged() D-Bus
* signal is emitted after the download finished successfully.
*
* @param url the URL which will be associated with the icon
@ -77,21 +77,39 @@ public Q_SLOTS: // dbus methods, called by the adaptor
/**
* Downloads the icon for a given host if it was not downloaded before
* or the download was too long ago. If the download finishes
* successfully, the iconChanged() DBUS signal is emitted.
* successfully, the iconChanged() D-Bus signal is emitted.
*
* @param url any URL on the host for which the icon is to be downloaded
*/
void downloadHostIcon(const KUrl &url);
signals: // DBUS signals
/**
* Emitting once a new icon is available, for a host or url
* Downloads the icon for a given host, even if we tried very recently.
* Not recommended in the general case; only useful for explicit "update favicon"
* actions from the user.
*
* If the download finishes successfully, the iconChanged() D-Bus signal is emitted.
*
* @param url any URL on the host for which the icon is to be downloaded
*/
void forceDownloadHostIcon(const KUrl &url);
signals: // D-Bus signals
/**
* Emitted once a new icon is available, for a host or url
*/
void iconChanged(bool isHost, QString hostOrURL, QString iconName);
/**
* Progress info while downloading an icon
*/
void infoMessage(QString iconURL, QString msg);
/**
* Emitted if an error occured while downloading the icon for the given host or url.
* You can usually ignore this (e.g. web browsers don't need to do anything if
* no favicon was found), but this signal can be useful in some cases, e.g.
* to let keditbookmarks know that it should move on to the next bookmark.
*/
void error(bool isHost, QString hostOrURL, QString errorString);
private:
void startDownload(const QString &, bool, const KUrl &);

View file

@ -10,6 +10,11 @@
<arg name="iconUrl" type="s" direction="out"/>
<arg name="msg" type="s" direction="out"/>
</signal>
<signal name="error">
<arg name="isHost" type="b" direction="out"/>
<arg name="hostOrUrl" type="s" direction="out"/>
<arg name="iconName" type="s" direction="out"/>
</signal>
<method name="iconForUrl">
<arg type="s" direction="out"/>
<arg name="url" type="s" direction="in"/>
@ -21,5 +26,8 @@
<method name="downloadHostIcon">
<arg name="url" type="s" direction="in"/>
</method>
<method name="forceDownloadHostIcon">
<arg name="url" type="s" direction="in"/>
</method>
</interface>
</node>