From 741d21719b942faa22a4bdf1a951d82d9fe73a68 Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Fri, 29 Jan 2010 11:58:52 +1000 Subject: Move image network access into a separate thread, with decoding. --- src/declarative/qml/qml.pri | 11 +- src/declarative/qml/qmlengine.cpp | 62 ++++-- src/declarative/qml/qmlengine.h | 8 +- src/declarative/qml/qmlengine_p.h | 3 + src/declarative/util/qmlpixmapcache.cpp | 321 +++++++++++++++++++++----------- src/declarative/util/qmlpixmapcache_p.h | 10 +- tools/qmlviewer/qmlviewer.cpp | 162 ++++++++++------ tools/qmlviewer/qmlviewer.h | 6 +- 8 files changed, 388 insertions(+), 195 deletions(-) diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 6f2f57f..cd2fbff 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -1,5 +1,4 @@ INCLUDEPATH += $$PWD - SOURCES += \ $$PWD/qmlparser.cpp \ $$PWD/qmlinstruction.cpp \ @@ -50,8 +49,8 @@ SOURCES += \ $$PWD/qmlvaluetypescriptclass.cpp \ $$PWD/qmltypenamescriptclass.cpp \ $$PWD/qmllistscriptclass.cpp \ - $$PWD/qmlworkerscript.cpp - + $$PWD/qmlworkerscript.cpp \ + $$PWD/qmlnetworkaccessmanagerfactory.cpp HEADERS += \ $$PWD/qmlparser_p.h \ $$PWD/qmlglobal_p.h \ @@ -117,10 +116,8 @@ HEADERS += \ $$PWD/qmllistscriptclass_p.h \ $$PWD/qmlworkerscript_p.h \ $$PWD/qmlscriptclass_p.h \ - $$PWD/qmlguard_p.h - + $$PWD/qmlguard_p.h \ + $$PWD/qmlnetworkaccessmanagerfactory.h QT += sql - include(parser/parser.pri) include(rewriter/rewriter.pri) - diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 5a40468..b3ac1bb 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -64,6 +64,7 @@ #include "qmlworkerscript_p.h" #include "qmlcomponent_p.h" #include "qmlscriptclass_p.h" +#include "qmlnetworkaccessmanagerfactory.h" #include @@ -121,7 +122,8 @@ QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) contextClass(0), sharedContext(0), sharedScope(0), objectClass(0), valueTypeClass(0), globalClass(0), cleanup(0), erroredBindings(0), inProgressCreations(0), scriptEngine(this), workerScriptEngine(0), componentAttacheds(0), inBeginCreate(false), - networkAccessManager(0), typeManager(e), uniqueId(1) + networkAccessManager(0), networkAccessManagerFactory(0), accessManagerValid(false), + typeManager(e), uniqueId(1) { globalClass = new QmlGlobalScriptClass(&scriptEngine); fileImportPath.append(QLibraryInfo::location(QLibraryInfo::DataPath)+QDir::separator()+QLatin1String("qml")); @@ -386,34 +388,64 @@ QmlContext *QmlEngine::rootContext() } /*! - Sets the common QNetworkAccessManager, \a network, used by all QML elements - instantiated by this engine. + Sets the \a factory to use for creating QNetworkAccessManager(s). + + QNetworkAccessManager is used for all network access by QML. + By implementing a factory it is possible to create custom + QNetworkAccessManager with specialized caching, proxy and + cookie support. +*/ +void QmlEngine::setNetworkAccessManagerFactory(QmlNetworkAccessManagerFactory *factory) +{ + Q_D(QmlEngine); + d->networkAccessManagerFactory = factory; +} - If the parent of \a network is this engine, the engine takes ownership and - will delete it as needed. Otherwise, ownership remains with the caller. +/*! + Returns the current QmlNetworkAccessManagerFactory. - This method should only be called before any QmlComponents are instantiated. + \sa setNetworkAccessManagerFactory() */ -void QmlEngine::setNetworkAccessManager(QNetworkAccessManager *network) +QmlNetworkAccessManagerFactory *QmlEngine::networkAccessManagerFactory() const +{ + Q_D(const QmlEngine); + return d->networkAccessManagerFactory; +} + +void QmlEngine::namInvalidated() { Q_D(QmlEngine); - if (d->networkAccessManager && d->networkAccessManager->parent() == this) - delete d->networkAccessManager; - d->networkAccessManager = network; + d->accessManagerValid = false; } /*! - Returns the common QNetworkAccessManager used by all QML elements + Returns a common QNetworkAccessManager which can be used by any QML element instantiated by this engine. - The default implements no caching, cookiejar, etc., just a default - QNetworkAccessManager. + If a QmlNetworkAccessManagerFactory has been set and a QNetworkAccessManager + has not yet been created, the QmlNetworkAccessManagerFactory will be used + to create the QNetworkAccessManager; otherwise the returned QNetworkAccessManager + will have no proxy or cache set. + + \sa setNetworkAccessManagerFactory() */ QNetworkAccessManager *QmlEngine::networkAccessManager() const { Q_D(const QmlEngine); - if (!d->networkAccessManager) - d->networkAccessManager = new QNetworkAccessManager(const_cast(this)); + if (!d->accessManagerValid) { + delete d->networkAccessManagerFactory; + d->networkAccessManagerFactory = 0; + } + if (!d->networkAccessManager) { + if (d->networkAccessManagerFactory) { + connect(d->networkAccessManagerFactory, SIGNAL(invalidated()) + , this, SLOT(namInvalidated()), Qt::UniqueConnection); + d->networkAccessManager = d->networkAccessManagerFactory->create(const_cast(this)); + } else { + d->networkAccessManager = new QNetworkAccessManager(const_cast(this)); + } + d->accessManagerValid = true; + } return d->networkAccessManager; } diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index 7cbbcac..b9ec277 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -63,6 +63,7 @@ class QUrl; class QScriptEngine; class QScriptContext; class QNetworkAccessManager; +class QmlNetworkAccessManagerFactory; class Q_DECLARATIVE_EXPORT QmlEngine : public QObject { Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) @@ -77,7 +78,9 @@ public: void addImportPath(const QString& dir); - void setNetworkAccessManager(QNetworkAccessManager *); + void setNetworkAccessManagerFactory(QmlNetworkAccessManagerFactory *); + QmlNetworkAccessManagerFactory *networkAccessManagerFactory() const; + QNetworkAccessManager *networkAccessManager() const; void setOfflineStoragePath(const QString& dir); @@ -92,6 +95,9 @@ public: Q_SIGNALS: void quit (); +private Q_SLOTS: + void namInvalidated(); + private: Q_DECLARE_PRIVATE(QmlEngine) }; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 85e8c80..6f62b40 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -92,6 +92,7 @@ class QmlValueTypeScriptClass; class QScriptEngineDebugger; class QNetworkReply; class QNetworkAccessManager; +class QmlNetworkAccessManagerFactory; class QmlAbstractBinding; class QScriptDeclarativeClass; class QmlTypeNameScriptClass; @@ -209,6 +210,8 @@ public: bool inBeginCreate; mutable QNetworkAccessManager *networkAccessManager; + mutable QmlNetworkAccessManagerFactory *networkAccessManagerFactory; + mutable bool accessManagerValid; QmlCompositeTypeManager typeManager; QStringList fileImportPath; diff --git a/src/declarative/util/qmlpixmapcache.cpp b/src/declarative/util/qmlpixmapcache.cpp index 4c1d448..c058408 100644 --- a/src/declarative/util/qmlpixmapcache.cpp +++ b/src/declarative/util/qmlpixmapcache.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qmlpixmapcache_p.h" +#include "qmlnetworkaccessmanagerfactory.h" #include "qfxperf_p_p.h" @@ -57,6 +58,15 @@ #include #include #include +#include + +#ifdef Q_OS_LINUX +#include +#include +#endif + +// Maximum number of simultaneous image requests to send. +static const int maxImageRequestCount = 8; QT_BEGIN_NAMESPACE @@ -69,105 +79,231 @@ class QmlImageReader : public QThread { Q_OBJECT public: - QmlImageReader(QObject *parent=0); + QmlImageReader(QmlEngine *eng); ~QmlImageReader(); - void read(QmlPixmapReply *rep); + QmlPixmapReply *getImage(const QUrl &url); void cancel(QmlPixmapReply *rep); + static QmlImageReader *instance(QmlEngine *engine); + protected: void run(); + bool event(QEvent *event); -private: - struct Job { - Job() : reply(0), error(false) {} - QmlPixmapReply *reply; - QImage img; - bool error; - }; +private slots: + void networkRequestDone(); + void namInvalidated() { + accessManagerValid = false; + } - void loadImage(Job &job); +private: + QNetworkAccessManager *networkAccessManager() { + if (!accessManagerValid) { + delete accessManager; + accessManager = 0; + } + if (!accessManager) { + if (engine && engine->networkAccessManagerFactory()) { + connect(engine->networkAccessManagerFactory(), SIGNAL(invalidated()) + , this, SLOT(namInvalidated()), Qt::UniqueConnection); + accessManager = engine->networkAccessManagerFactory()->create(this); + } else { + accessManager = new QNetworkAccessManager(this); + } + accessManagerValid = true; + } + return accessManager; + } - QList jobs; + QList jobs; + QList cancelled; + QHash replies; + QNetworkAccessManager *accessManager; + bool accessManagerValid; + QmlEngine *engine; QMutex mutex; - QWaitCondition haveJob; - bool quit; + static QHash readers; }; -class QmlImageDecodeEvent : public QEvent +QHash QmlImageReader::readers; + +class QmlImageReaderEvent : public QEvent { public: - QmlImageDecodeEvent(bool err, QImage &img) : QEvent(QEvent::User), error(err), image(img) {} + enum ReadError { NoError, Loading, Decoding }; - bool error; + QmlImageReaderEvent(QmlImageReaderEvent::ReadError err, QImage &img) + : QEvent(QEvent::User), error(err), image(img) {} + + ReadError error; QImage image; }; -Q_GLOBAL_STATIC(QmlImageReader, qmlImageReader) - -QmlImageReader::QmlImageReader(QObject *parent) : QThread(parent), quit(false) +QmlImageReader::QmlImageReader(QmlEngine *eng) + : QThread(eng), accessManager(0), accessManagerValid(false), engine(eng) { start(QThread::LowPriority); } QmlImageReader::~QmlImageReader() { - quit = true; - haveJob.wakeOne(); } -void QmlImageReader::read(QmlPixmapReply *reply) +QmlImageReader *QmlImageReader::instance(QmlEngine *engine) +{ + QmlImageReader *reader = readers.value(engine); + if (!reader) { + static QMutex rmutex; + rmutex.lock(); + reader = new QmlImageReader(engine); + readers.insert(engine, reader); + rmutex.unlock(); + } + + return reader; +} + +QmlPixmapReply *QmlImageReader::getImage(const QUrl &url) { mutex.lock(); - Job job; - job.reply = reply; - jobs.append(job); + QmlPixmapReply *reply = new QmlPixmapReply(engine, url); + jobs.append(reply); if (jobs.count() == 1) - haveJob.wakeOne(); + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); mutex.unlock(); + return reply; } void QmlImageReader::cancel(QmlPixmapReply *reply) { mutex.lock(); - QList::iterator it = jobs.begin(); - while (it != jobs.end()) { - if ((*it).reply == reply) { - jobs.erase(it); - break; + if (reply->isLoading()) { + // Already requested. Add to cancel list to be cancelled in reader thread. + cancelled.append(reply); + if (cancelled.count() == 1) + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + } else { + // Not yet processed - just remove from waiting list + QList::iterator it = jobs.begin(); + while (it != jobs.end()) { + QmlPixmapReply *job = *it; + if (job == reply) { + jobs.erase(it); + break; + } + ++it; } - ++it; } mutex.unlock(); } -void QmlImageReader::loadImage(Job &job) +void QmlImageReader::run() { - QImageReader imgio(job.reply->device()); - if (imgio.read(&job.img)) { - job.error = false; - } else { - job.error = true; - qWarning() << imgio.errorString(); +#ifdef Q_OS_LINUX + struct sched_param param; + int policy; + + pthread_getschedparam(pthread_self(), &policy, ¶m); + pthread_setschedparam(pthread_self(), SCHED_IDLE, ¶m); +#endif + + exec(); +} + +bool QmlImageReader::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + static int replyDownloadProgress = -1; + static int replyFinished = -1; + static int downloadProgress = -1; + static int thisNetworkRequestDone = -1; + + if (replyDownloadProgress == -1) { + replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + thisNetworkRequestDone = QmlImageReader::staticMetaObject.indexOfSlot("networkRequestDone()"); + } + + while (1) { + mutex.lock(); + + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QmlPixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + replies.remove(reply); + reply->close(); + job->release(true); + } + } + cancelled.clear(); + } + + if (!accessManagerValid) { + // throw away existing requests and reschedule. + QHash::iterator it = replies.begin(); + for (; it != replies.end(); ++it) { + delete it.key(); + jobs.prepend(*it); + } + replies.clear(); + } + + if (!jobs.count() || replies.count() > maxImageRequestCount) { + mutex.unlock(); + break; + } + + QmlPixmapReply *runningJob = jobs.takeFirst(); + runningJob->addRef(); + runningJob->setLoading(); + QUrl url = runningJob->url(); + mutex.unlock(); + + // fetch + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, this, thisNetworkRequestDone); + + replies.insert(reply, runningJob); + } + return true; } + + return QObject::event(event); } -void QmlImageReader::run() +void QmlImageReader::networkRequestDone() { - while (1) { - mutex.lock(); - if (!jobs.count()) - haveJob.wait(&mutex); - if (quit) - break; - Job runningJob = jobs.takeFirst(); - runningJob.reply->addRef(); - mutex.unlock(); - - loadImage(runningJob); - QCoreApplication::postEvent(runningJob.reply, new QmlImageDecodeEvent(runningJob.error, runningJob.img)); + QNetworkReply *reply = static_cast(sender()); + QmlPixmapReply *job = replies.take(reply); + if (job) { + QImage image; + QmlImageReaderEvent::ReadError error; + if (reply->error()) { + error = QmlImageReaderEvent::Loading; + } else { + QImageReader imgio(reply); + if (imgio.read(&image)) { + error = QmlImageReaderEvent::NoError; + } else { + error = QmlImageReaderEvent::Decoding; + } + } + // send completion event to the QmlPixmapReply + QCoreApplication::postEvent(job, new QmlImageReaderEvent(error, image)); } + // kick off event loop again if we have dropped below max request count + if (replies.count() == maxImageRequestCount) + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + reply->deleteLater(); } static bool readImage(QIODevice *dev, QPixmap *pixmap) @@ -228,46 +364,26 @@ class QmlPixmapReplyPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QmlPixmapReply) public: - QmlPixmapReplyPrivate(const QUrl &u, QNetworkReply *r) - : QObjectPrivate(), refCount(1), url(u), reply(r), status(QmlPixmapReply::Loading) { + QmlPixmapReplyPrivate(QmlEngine *e, const QUrl &u) + : QObjectPrivate(), refCount(1), url(u), status(QmlPixmapReply::Loading), loading(false), engine(e) { } int refCount; QUrl url; - QNetworkReply *reply; QPixmap pixmap; // ensure reference to pixmap so QPixmapCache does not discard - QImage image; QmlPixmapReply::Status status; + bool loading; + QmlEngine *engine; }; -QmlPixmapReply::QmlPixmapReply(const QUrl &url, QNetworkReply *reply) - : QObject(*new QmlPixmapReplyPrivate(url, reply), 0) +QmlPixmapReply::QmlPixmapReply(QmlEngine *engine, const QUrl &url) + : QObject(*new QmlPixmapReplyPrivate(engine, url), 0) { - Q_D(QmlPixmapReply); - - static int replyDownloadProgress = -1; - static int replyFinished = -1; - static int thisDownloadProgress = -1; - static int thisNetworkRequestDone = -1; - - if (replyDownloadProgress == -1) { - replyDownloadProgress = QNetworkReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - replyFinished = QNetworkReply::staticMetaObject.indexOfSignal("finished()"); - thisDownloadProgress = QmlPixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); - thisNetworkRequestDone = QmlPixmapReply::staticMetaObject.indexOfSlot("networkRequestDone()"); - } - - QMetaObject::connect(d->reply, replyDownloadProgress, this, thisDownloadProgress, Qt::DirectConnection); - QMetaObject::connect(d->reply, replyFinished, this, thisNetworkRequestDone); } QmlPixmapReply::~QmlPixmapReply() { - Q_D(QmlPixmapReply); - if (d->status == Decoding) - qmlImageReader()->cancel(this); - delete d->reply; } const QUrl &QmlPixmapReply::url() const @@ -276,36 +392,16 @@ const QUrl &QmlPixmapReply::url() const return d->url; } -void QmlPixmapReply::networkRequestDone() -{ - Q_D(QmlPixmapReply); - if (d->reply->error()) { - d->pixmap = QPixmap(); - d->status = Error; - QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); - QString strKey = QString::fromLatin1(key.constData(), key.count()); - QPixmapCache::insert(strKey, d->pixmap); - qWarning() << "Network error loading" << d->url << d->reply->errorString(); - emit finished(); - } else { - qmlImageReader()->read(this); - d->status = Decoding; - } -} - bool QmlPixmapReply::event(QEvent *event) { Q_D(QmlPixmapReply); if (event->type() == QEvent::User) { + d->loading = false; if (!release(true)) { - QmlImageDecodeEvent *de = static_cast(event); - d->status = de->error ? Error : Ready; - if (d->status == Ready) { + QmlImageReaderEvent *de = static_cast(event); + d->status = (de->error == QmlImageReaderEvent::NoError) ? Ready : Error; + if (d->status == Ready) d->pixmap = QPixmap::fromImage(de->image); - d->image = QImage(); - } else { - qWarning() << "Error decoding" << d->url; - } QByteArray key = d->url.toEncoded(QUrl::FormattingOption(0x100)); QString strKey = QString::fromLatin1(key.constData(), key.count()); QPixmapCache::insert(strKey, d->pixmap); @@ -323,10 +419,16 @@ QmlPixmapReply::Status QmlPixmapReply::status() const return d->status; } -QIODevice *QmlPixmapReply::device() +bool QmlPixmapReply::isLoading() const +{ + Q_D(const QmlPixmapReply); + return d->loading; +} + +void QmlPixmapReply::setLoading() { Q_D(QmlPixmapReply); - return d->reply; + d->loading = true; } void QmlPixmapReply::addRef() @@ -342,11 +444,17 @@ bool QmlPixmapReply::release(bool defer) --d->refCount; if (d->refCount == 0) { qmlActivePixmapReplies()->remove(d->url); + if (d->status == Loading && !d->loading) + QmlImageReader::instance(d->engine)->cancel(this); if (defer) deleteLater(); else delete this; return true; + } else if (d->refCount == 1 && d->loading) { + // The only reference left is the reader thread. + qmlActivePixmapReplies()->remove(d->url); + QmlImageReader::instance(d->engine)->cancel(this); } return false; @@ -418,9 +526,8 @@ QmlPixmapReply *QmlPixmapCache::request(QmlEngine *engine, const QUrl &url) { QmlPixmapReplyHash::Iterator iter = qmlActivePixmapReplies()->find(url); if (iter == qmlActivePixmapReplies()->end()) { - QNetworkRequest req(url); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - QmlPixmapReply *item = new QmlPixmapReply(url, engine->networkAccessManager()->get(req)); + QmlImageReader *reader = QmlImageReader::instance(engine); + QmlPixmapReply *item = reader->getImage(url); iter = qmlActivePixmapReplies()->insert(url, item); } else { (*iter)->addRef(); diff --git a/src/declarative/util/qmlpixmapcache_p.h b/src/declarative/util/qmlpixmapcache_p.h index 711e902..0140352 100644 --- a/src/declarative/util/qmlpixmapcache_p.h +++ b/src/declarative/util/qmlpixmapcache_p.h @@ -59,10 +59,10 @@ class Q_DECLARATIVE_EXPORT QmlPixmapReply : public QObject { Q_OBJECT public: - QmlPixmapReply(const QUrl &url, QNetworkReply *reply); + QmlPixmapReply(QmlEngine *engine, const QUrl &url); ~QmlPixmapReply(); - enum Status { Ready, Error, Unrequested, Loading, Decoding }; + enum Status { Ready, Error, Unrequested, Loading }; Status status() const; const QUrl &url() const; @@ -75,12 +75,10 @@ protected: bool event(QEvent *event); private: - QIODevice *device(); void addRef(); bool release(bool defer=false); - -private Q_SLOTS: - void networkRequestDone(); + bool isLoading() const; + void setLoading(); private: Q_DISABLE_COPY(QmlPixmapReply) diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp index 7dfecc8..71ba81c 100644 --- a/tools/qmlviewer/qmlviewer.cpp +++ b/tools/qmlviewer/qmlviewer.cpp @@ -45,6 +45,7 @@ #include "qmlviewer.h" #include #include +#include #include "qml.h" #include #include @@ -80,6 +81,8 @@ #include #include #include +#include +#include #include "proxysettings.h" #include "deviceorientation.h" @@ -298,9 +301,22 @@ public: PersistentCookieJar(QObject *parent) : QNetworkCookieJar(parent) { load(); } ~PersistentCookieJar() { save(); } + virtual QList cookiesForUrl(const QUrl &url) const + { + QMutexLocker lock(&mutex); + return QNetworkCookieJar::cookiesForUrl(url); + } + + virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url) + { + QMutexLocker lock(&mutex); + return QNetworkCookieJar::setCookiesFromUrl(cookieList, url); + } + private: void save() { + QMutexLocker lock(&mutex); QList list = allCookies(); QByteArray data; foreach (QNetworkCookie cookie, list) { @@ -315,12 +331,90 @@ private: void load() { + QMutexLocker lock(&mutex); QSettings settings("Nokia", "QtQmlViewer"); QByteArray data = settings.value("Cookies").toByteArray(); setAllCookies(QNetworkCookie::parseCookies(data)); } + + mutable QMutex mutex; }; +class NetworkAccessManagerFactory : public QmlNetworkAccessManagerFactory +{ +public: + NetworkAccessManagerFactory() : cookieJar(0), cacheSize(0) {} + + QNetworkAccessManager *create(QObject *parent) { + QMutexLocker lock(&mutex); + QNetworkAccessManager *manager = new QNetworkAccessManager(parent); + if (!cookieJar) + cookieJar = new PersistentCookieJar(this); + manager->setCookieJar(cookieJar); + cookieJar->setParent(this); + setupProxy(manager); + if (cacheSize > 0) { + QNetworkDiskCache *cache = new QNetworkDiskCache; + cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-duiviewer-network-cache")); + cache->setMaximumCacheSize(cacheSize); + manager->setCache(cache); + } else { + manager->setCache(0); + } + qDebug() << "created new manager for" << parent; + return manager; + } + + void setupProxy(QNetworkAccessManager *nam) + { + class SystemProxyFactory : public QNetworkProxyFactory + { + public: + virtual QList queryProxy(const QNetworkProxyQuery &query) + { + QString protocolTag = query.protocolTag(); + if (httpProxyInUse && (protocolTag == "http" || protocolTag == "https")) { + QList ret; + ret << httpProxy; + return ret; + } + return QNetworkProxyFactory::systemProxyForQuery(query); + } + void setHttpProxy (QNetworkProxy proxy) + { + httpProxy = proxy; + httpProxyInUse = true; + } + void unsetHttpProxy () + { + httpProxyInUse = false; + } + private: + bool httpProxyInUse; + QNetworkProxy httpProxy; + }; + + SystemProxyFactory *proxyFactory = new SystemProxyFactory; + if (ProxySettings::httpProxyInUse()) + proxyFactory->setHttpProxy(ProxySettings::httpProxy()); + else + proxyFactory->unsetHttpProxy(); + nam->setProxyFactory(proxyFactory); + } + + void setCacheSize(int size) { + if (size != cacheSize) { + cacheSize = size; + invalidate(); + } + } + + PersistentCookieJar *cookieJar; + QMutex mutex; + int cacheSize; +}; + + QString QmlViewer::getVideoFileName() { QString title = convertAvailable || ffmpegAvailable ? tr("Save Video File") : tr("Save PNG Frames"); @@ -397,8 +491,8 @@ QmlViewer::QmlViewer(QWidget *parent, Qt::WindowFlags flags) setCentralWidget(canvas); #endif - setupProxy(); - canvas->engine()->networkAccessManager()->setCookieJar(new PersistentCookieJar(this)); + namFactory = new NetworkAccessManagerFactory; + canvas->engine()->setNetworkAccessManagerFactory(namFactory); connect(&autoStartTimer, SIGNAL(triggered()), this, SLOT(autoStartRecording())); connect(&autoStopTimer, SIGNAL(triggered()), this, SLOT(autoStopRecording())); @@ -409,6 +503,12 @@ QmlViewer::QmlViewer(QWidget *parent, Qt::WindowFlags flags) recordTimer.setRepeating(true); } +QmlViewer::~QmlViewer() +{ + canvas->engine()->setNetworkAccessManagerFactory(0); + delete namFactory; +} + void QmlViewer::adjustSizeSlot() { resize(sizeHint()); @@ -591,7 +691,7 @@ void QmlViewer::showProxySettings() void QmlViewer::proxySettingsChanged() { - setupProxy (); + namFactory->invalidate(); reload (); } @@ -1325,63 +1425,9 @@ void QmlViewer::setDeviceKeys(bool on) devicemode = on; } -void QmlViewer::setupProxy() -{ - class SystemProxyFactory : public QNetworkProxyFactory - { - public: - virtual QList queryProxy(const QNetworkProxyQuery &query) - { - QString protocolTag = query.protocolTag(); - if (httpProxyInUse && (protocolTag == "http" || protocolTag == "https")) { - QList ret; - ret << httpProxy; - return ret; - } - return QNetworkProxyFactory::systemProxyForQuery(query); - } - void setHttpProxy (QNetworkProxy proxy) - { - httpProxy = proxy; - httpProxyInUse = true; - } - void unsetHttpProxy () - { - httpProxyInUse = false; - } - private: - bool httpProxyInUse; - QNetworkProxy httpProxy; - }; - - QNetworkAccessManager * nam = canvas->engine()->networkAccessManager(); - SystemProxyFactory *proxyFactory = new SystemProxyFactory; - if (ProxySettings::httpProxyInUse()) - proxyFactory->setHttpProxy(ProxySettings::httpProxy()); - else - proxyFactory->unsetHttpProxy(); - nam->setProxyFactory(proxyFactory); -} - void QmlViewer::setNetworkCacheSize(int size) { - QNetworkAccessManager * nam = canvas->engine()->networkAccessManager(); - QNetworkDiskCache *cache = qobject_cast(nam->cache()); - if (!cache) { - if (size==0) - return; - cache = new QNetworkDiskCache; - cache->setCacheDirectory(QDir::tempPath()+QLatin1String("/qml-duiviewer-network-cache")); - nam->setCache(cache); - } - if (size == cache->maximumCacheSize()) - return; - if (size>0) { - // Setup a caching network manager - cache->setMaximumCacheSize(size); - } else { - nam->setCache(0); - } + namFactory->setCacheSize(size); } void QmlViewer::setUseGL(bool useGL) diff --git a/tools/qmlviewer/qmlviewer.h b/tools/qmlviewer/qmlviewer.h index 3b14d39..6b05584 100644 --- a/tools/qmlviewer/qmlviewer.h +++ b/tools/qmlviewer/qmlviewer.h @@ -57,6 +57,8 @@ class QProcess; class RecordingDialog; class QmlGraphicsTester; class QNetworkReply; +class QNetworkCookieJar; +class NetworkAccessManagerFactory; class QmlViewer #if defined(Q_OS_SYMBIAN) @@ -68,6 +70,7 @@ class QmlViewer Q_OBJECT public: QmlViewer(QWidget *parent=0, Qt::WindowFlags flags=0); + ~QmlViewer(); enum ScriptOption { Play = 0x00000001, @@ -138,7 +141,6 @@ private slots: void unpackWgt(); private: - void setupProxy(); QString getVideoFileName(); PreviewDeviceSkin *skin; @@ -181,6 +183,8 @@ private: QNetworkReply *wgtreply; QString wgtdir; + NetworkAccessManagerFactory *namFactory; + bool useQmlFileBrowser; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QmlViewer::ScriptOptions) -- cgit v0.12