diff options
57 files changed, 1601 insertions, 871 deletions
diff --git a/demos/browser/networkaccessmanager.h b/demos/browser/networkaccessmanager.h index 381cb50..4c4603e 100644 --- a/demos/browser/networkaccessmanager.h +++ b/demos/browser/networkaccessmanager.h @@ -43,6 +43,7 @@ #define NETWORKACCESSMANAGER_H #include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkRequest> class NetworkAccessManager : public QNetworkAccessManager { @@ -51,6 +52,14 @@ class NetworkAccessManager : public QNetworkAccessManager public: NetworkAccessManager(QObject *parent = 0); + // this is a temporary hack until we properly use the pipelining flags from QtWebkit + // pipeline everything! :) + virtual QNetworkReply* createRequest ( Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0 ) { + QNetworkRequest request = req; // copy so we can modify + request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + return QNetworkAccessManager::createRequest(op, request, outgoingData); + } + private: QList<QString> sslTrustedHostList; diff --git a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri index b43602e..d65095d 100644 --- a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri +++ b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri @@ -128,7 +128,7 @@ SOURCES += \ yarr/RegexJIT.cpp \ interpreter/RegisterFile.cpp -win32-*: SOURCES += jit/ExecutableAllocatorWin.cpp +win32-*|wince*: SOURCES += jit/ExecutableAllocatorWin.cpp else: SOURCES += jit/ExecutableAllocatorPosix.cpp # AllInOneFile.cpp helps gcc analize and optimize code diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index fc11815..696a95a 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -137,7 +137,6 @@ QT_BEGIN_NAMESPACE \sa currentValue */ - static bool animationValueLessThan(const QVariantAnimation::KeyValue &p1, const QVariantAnimation::KeyValue &p2) { return p1.first < p2.first; @@ -178,11 +177,8 @@ template<> Q_INLINE_TEMPLATE QLineF _q_interpolate(const QLineF &f, const QLineF return QLineF( _q_interpolate(f.p1(), t.p1(), progress), _q_interpolate(f.p2(), t.p2(), progress)); } -QVariantAnimationPrivate::QVariantAnimationPrivate() : duration(250), interpolator(&defaultInterpolator), - changedSignalMask(1 << QVariantAnimation::staticMetaObject.indexOfSignal("valueChanged(QVariant)")) -{ - //we keep the mask so that we emit valueChanged only when needed (for performance reasons) -} +QVariantAnimationPrivate::QVariantAnimationPrivate() : duration(250), interpolator(&defaultInterpolator) +{ } void QVariantAnimationPrivate::convertValues(int t) { @@ -278,7 +274,12 @@ void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) localProgress); qSwap(currentValue, ret); q->updateCurrentValue(currentValue); - if ((connectedSignals[0] & changedSignalMask) && currentValue != ret) { + static QBasicAtomicInt changedSignalIndex = Q_BASIC_ATOMIC_INITIALIZER(0); + if (!changedSignalIndex) { + //we keep the mask so that we emit valueChanged only when needed (for performance reasons) + changedSignalIndex.testAndSetRelaxed(0, signalIndex("valueChanged(QVariant)")); + } + if (isSignalConnected(changedSignalIndex) && currentValue != ret) { //the value has changed emit q->valueChanged(currentValue); } diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h index ef57a4c..ce625f1 100644 --- a/src/corelib/animation/qvariantanimation_p.h +++ b/src/corelib/animation/qvariantanimation_p.h @@ -93,8 +93,6 @@ public: QVariantAnimation::Interpolator interpolator; - const quint32 changedSignalMask; - void setCurrentValueForProgress(const qreal progress); void recalculateCurrentInterval(bool force=false); void setValueAt(qreal, const QVariant &); diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index b150e22..52c507d 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -696,6 +696,7 @@ static void setup() (void)new QJisCodec; (void)new QSjisCodec; (void)new QEucKrCodec; + (void)new QCP949Codec; (void)new QBig5Codec; (void)new QBig5hkscsCodec; # endif // QT_NO_ICONV && !QT_BOOTSTRAPPED diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index f42a2ff..20e7845 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -487,7 +487,7 @@ QT_END_NAMESPACE extern const char qt_core_interpreter[] __attribute__((section(".interp"))) = ELF_INTERPRETER; -extern "C" +extern "C" void qt_core_boilerplate(); void qt_core_boilerplate() { printf("This is the QtCore library version " QT_VERSION_STR "\n" diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index ca178ce..fd1e367 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2445,7 +2445,7 @@ QDebug operator<<(QDebug debug, QDir::Filters filters) return debug; } -QDebug operator<<(QDebug debug, QDir::SortFlags sorting) +static QDebug operator<<(QDebug debug, QDir::SortFlags sorting) { if (sorting == QDir::NoSort) { debug << "QDir::SortFlags(NoSort)"; diff --git a/src/corelib/kernel/qabstracteventdispatcher_p.h b/src/corelib/kernel/qabstracteventdispatcher_p.h index 86c9d79..dc69265 100644 --- a/src/corelib/kernel/qabstracteventdispatcher_p.h +++ b/src/corelib/kernel/qabstracteventdispatcher_p.h @@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE +Q_CORE_EXPORT uint qGlobalPostedEventsCount(); + class Q_CORE_EXPORT QAbstractEventDispatcherPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QAbstractEventDispatcher) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 522f0dc..847938f 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -140,63 +140,6 @@ QT_BEGIN_NAMESPACE \value Public */ -// do not touch without touching the moc as well -enum PropertyFlags { - Invalid = 0x00000000, - Readable = 0x00000001, - Writable = 0x00000002, - Resettable = 0x00000004, - EnumOrFlag = 0x00000008, - StdCppSet = 0x00000100, -// Override = 0x00000200, - Constant = 0x00000400, - Final = 0x00000800, - Designable = 0x00001000, - ResolveDesignable = 0x00002000, - Scriptable = 0x00004000, - ResolveScriptable = 0x00008000, - Stored = 0x00010000, - ResolveStored = 0x00020000, - Editable = 0x00040000, - ResolveEditable = 0x00080000, - User = 0x00100000, - ResolveUser = 0x00200000, - Notify = 0x00400000 -}; - -enum MethodFlags { - AccessPrivate = 0x00, - AccessProtected = 0x01, - AccessPublic = 0x02, - AccessMask = 0x03, //mask - - MethodMethod = 0x00, - MethodSignal = 0x04, - MethodSlot = 0x08, - MethodConstructor = 0x0c, - MethodTypeMask = 0x0c, - - MethodCompatibility = 0x10, - MethodCloned = 0x20, - MethodScriptable = 0x40 -}; - -enum MetaObjectFlags { - DynamicMetaObject = 0x01 -}; - -struct QMetaObjectPrivate -{ - int revision; - int className; - int classInfoCount, classInfoData; - int methodCount, methodData; - int propertyCount, propertyData; - int enumeratorCount, enumeratorData; - int constructorCount, constructorData; - int flags; -}; - static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } @@ -599,29 +542,46 @@ int QMetaObject::indexOfMethod(const char *method) const */ int QMetaObject::indexOfSignal(const char *signal) const { - int i = -1; const QMetaObject *m = this; - while (m && i < 0) { + int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +/*! \internal + Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. + + \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found +*/ +int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal) +{ + int i = -1; + while (*baseObject) { + const QMetaObject *const m = *baseObject; for (i = priv(m->d.data)->methodCount-1; i >= 0; --i) if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal && strcmp(signal, m->d.stringdata - + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { - i += m->methodOffset(); + + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { break; } - m = m->d.superdata; + if (i >= 0) + break; + *baseObject = m->d.superdata; } #ifndef QT_NO_DEBUG + const QMetaObject *m = *baseObject; if (i >= 0 && m && m->d.superdata) { int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) qWarning("QMetaObject::indexOfSignal:%s: Conflict with %s::%s", - m->d.stringdata, m->d.superdata->d.stringdata, signal); + m->d.stringdata, m->d.superdata->d.stringdata, signal); } #endif return i; } + /*! Finds \a slot and returns its index; otherwise returns -1. @@ -2669,4 +2629,20 @@ const char* QMetaClassInfo::value() const and \a data. */ +/*! \internal + If the local_method_index is a cloned method, return the index of the original. + + Example: if the index of "destroyed()" is passed, the index of "destroyed(QObject*)" is returned + */ +int QMetaObjectPrivate::originalClone(const QMetaObject *mobj, int local_method_index) +{ + int handle = get(mobj)->methodData + 5 * local_method_index; + while (mobj->d.data[handle + 4] & MethodCloned) { + Q_ASSERT(local_method_index > 0); + handle -= 5; + local_method_index--; + } + return local_method_index; +} + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 66ed55c..d843deb 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -54,9 +54,82 @@ // #include <QtCore/qglobal.h> +#include <QtCore/qobjectdefs.h> QT_BEGIN_NAMESPACE +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000 +}; + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 +}; + +enum MetaObjectFlags { + DynamicMetaObject = 0x01 +}; + + +struct QMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + int constructorCount, constructorData; //since revision 2 + int flags; //since revision 3 + int signalCount; //since revision 4 + + static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) + { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } + + static int indexOfSignalRelative(const QMetaObject **baseObject, const char* name); + static int originalClone(const QMetaObject *obj, int local_method_index); + + //defined in qobject.cpp + static bool connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type = 0, int *types = 0); + static bool disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index); + +}; + #ifndef UTILS_H // mirrored in moc's utils.h static inline bool is_ident_char(char s) @@ -202,6 +275,7 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc return result; } + QT_END_NAMESPACE #endif diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 0e75867..022ae13 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -41,6 +41,7 @@ #include "qobject.h" #include "qobject_p.h" +#include "qmetaobject_p.h" #include "qabstracteventdispatcher.h" #include "qcoreapplication.h" @@ -221,12 +222,42 @@ void QObjectPrivate::removePendingChildInsertedEvents(QObject *child) #endif +/*!\internal + For a given metaobject, compute the signal offset, and the method offset (including signals) +*/ +static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset) +{ + *signalOffset = *methodOffset = 0; + const QMetaObject *m = metaobject->d.superdata; + while (m) { + const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m); + *methodOffset += d->methodCount; + *signalOffset += (d->revision >= 4) ? d->signalCount : d->methodCount; + /*Before Qt 4.6 (revision 4), the signalCount information was not generated by moc. + so for compatibility we consider all the method as slot for old moc output*/ + m = m->d.superdata; + } +} + +/*! \internal + This vector contains the all connections from an object. + + Each object may have one vector containing the lists of connections for a given signal. + The index in the vector correspond to the signal index. + The signal index is the one returned by QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal). + Negative index means connections to all signals. + + This vector is protected by the object mutex (signalSlotMutexes()) + + Each Connection is also part of a 'senders' linked list. The mutex of the receiver must be locked when touching + the pointers of this linked list. +*/ class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList> { public: - bool orphaned; - bool dirty; - int inUse; + bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse + bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet + int inUse; //number of functions that are currently accessing this object or its connections QObjectPrivate::ConnectionList allsignals; QObjectConnectionListVector() @@ -251,7 +282,7 @@ public: bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const { Q_Q(const QObject); - int signal_index = q->metaObject()->indexOfSignal(signal); + int signal_index = signalIndex(signal); if (signal_index < 0) return false; QMutexLocker locker(signalSlotLock(q)); @@ -275,7 +306,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const { Q_Q(const QObject); QObjectList returnValue; - int signal_index = q->metaObject()->indexOfSignal(signal); + int signal_index = signalIndex(signal); if (signal_index < 0) return returnValue; QMutexLocker locker(signalSlotLock(q)); @@ -2417,6 +2448,7 @@ QObject *QObject::sender() const int QObject::receivers(const char *signal) const { + Q_D(const QObject); int receivers = 0; if (signal) { QByteArray signal_name = QMetaObject::normalizedSignature(signal); @@ -2426,8 +2458,7 @@ int QObject::receivers(const char *signal) const return 0; #endif signal++; // skip code - const QMetaObject *smeta = this->metaObject(); - int signal_index = smeta->indexOfSignal(signal); + int signal_index = d->signalIndex(signal); if (signal_index < 0) { #ifndef QT_NO_DEBUG err_method_notfound(this, signal-1, "receivers"); @@ -2551,19 +2582,26 @@ bool QObject::connect(const QObject *sender, const char *signal, const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code - int signal_index = smeta->indexOfSignal(signal); + int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; - signal_index = smeta->indexOfSignal(signal); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); + if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } } + signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); + int signalOffset, methodOffset; + computeOffsets(smeta, &signalOffset, &methodOffset); + int signal_absolute_index = signal_index + methodOffset; + signal_index += signalOffset; QByteArray tmp_method_name; int membcode = extract_code(method); @@ -2612,12 +2650,12 @@ bool QObject::connect(const QObject *sender, const char *signal, int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) - && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes()))) + && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) return false; #ifndef QT_NO_DEBUG { - QMetaMethod smethod = smeta->method(signal_index); + QMetaMethod smethod = smeta->method(signal_absolute_index); QMetaMethod rmethod = rmeta->method(method_index); if (warnCompat) { if(smethod.attributes() & QMetaMethod::Compatibility) { @@ -2630,7 +2668,7 @@ bool QObject::connect(const QObject *sender, const char *signal, } } #endif - if (!QMetaObject::connect(sender, signal_index, receiver, method_index, type, types)) + if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) return false; const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; @@ -2761,14 +2799,18 @@ bool QObject::disconnect(const QObject *sender, const char *signal, do { int signal_index = -1; if (signal) { - signal_index = smeta->indexOfSignal(signal); - if (signal_index < smeta->methodOffset()) - continue; + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); + if (signal_index < 0) + break; + signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); + int signalOffset, methodOffset; + computeOffsets(smeta, &signalOffset, &methodOffset); + signal_index += signalOffset; signal_found = true; } if (!method) { - res |= QMetaObject::disconnect(sender, signal_index, receiver, -1); + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1); } else { const QMetaObject *rmeta = receiver->metaObject(); do { @@ -2778,7 +2820,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal, rmeta = rmeta->superClass(); if (method_index < 0) break; - res |= QMetaObject::disconnect(sender, signal_index, receiver, method_index); + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index); method_found = true; } while ((rmeta = rmeta->superClass())); } @@ -2869,16 +2911,31 @@ void QObject::disconnectNotify(const char *) } /*!\internal + \a types is a 0-terminated vector of meta types for queued + connections. - \a types is a 0-terminated vector of meta types for queued - connections. - - if \a signal_index is -1, then we effectively connect *all* signals - from the sender to the receiver's slot -*/ + if \a signal_index is -1, then we effectively connect *all* signals + from the sender to the receiver's slot + */ bool QMetaObject::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types) { + const QMetaObject *mo = sender->metaObject(); + while (mo && mo->methodOffset() > signal_index) + mo = mo->superClass(); + int signalOffset, methodOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + signal_index = QMetaObjectPrivate::originalClone(mo, signal_index - methodOffset) + signalOffset; + return QMetaObjectPrivate::connect(sender, signal_index, + receiver, method_index, type, types); +} + +/*! \internal + Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex + */ +bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, int type, int *types) +{ QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); @@ -2886,7 +2943,7 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, signalSlotLock(receiver)); if (type & Qt::UniqueConnection) { - QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; + QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; if (connectionLists && connectionLists->count() > signal_index) { const QObjectPrivate::Connection *c2 = (*connectionLists)[signal_index].first; @@ -2907,25 +2964,25 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, c->connectionType = type; c->argumentTypes = types; c->nextConnectionList = 0; - c->prev = &r->d_func()->senders; + c->prev = &(QObjectPrivate::get(r)->senders); c->next = *c->prev; *c->prev = c; if (c->next) c->next->prev = &c->next; - s->d_func()->addConnection(signal_index, c); + QObjectPrivate::get(s)->addConnection(signal_index, c); + QObjectPrivate *const sender_d = QObjectPrivate::get(s); if (signal_index < 0) { - for (uint i = 0; i < (sizeof sender->d_func()->connectedSignals - / sizeof sender->d_func()->connectedSignals[0] ); ++i) - sender->d_func()->connectedSignals[i] = ~0u; - } else if (signal_index < (int)sizeof sender->d_func()->connectedSignals * 8) { - uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - sender->d_func()->connectedSignals[n] |= (1 << (signal_index - n * 8 - * sizeof sender->d_func()->connectedSignals[0])); + for (uint i = 0; i < (sizeof sender_d->connectedSignals + / sizeof sender_d->connectedSignals[0] ); ++i) + sender_d->connectedSignals[i] = ~0u; + } else if (signal_index < (int)sizeof sender_d->connectedSignals * 8) { + uint n = (signal_index / (8 * sizeof sender_d->connectedSignals[0])); + sender_d->connectedSignals[n] |= (1 << (signal_index - n * 8 + * sizeof sender_d->connectedSignals[0])); } - return true; } @@ -2935,6 +2992,22 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, bool QMetaObject::disconnect(const QObject *sender, int signal_index, const QObject *receiver, int method_index) { + const QMetaObject *mo = sender->metaObject(); + while (mo && mo->methodOffset() > signal_index) + mo = mo->superClass(); + int signalOffset, methodOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + signal_index = QMetaObjectPrivate::originalClone(mo, signal_index - methodOffset) + signalOffset; + return QMetaObjectPrivate::disconnect(sender, signal_index, + receiver, method_index); +} + +/*! \internal + Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex + */ +bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index) +{ if (!sender) return false; @@ -2945,7 +3018,7 @@ bool QMetaObject::disconnect(const QObject *sender, int signal_index, QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; QOrderedMutexLocker locker(senderMutex, receiverMutex); - QObjectConnectionListVector *connectionLists = s->d_func()->connectionLists; + QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; if (!connectionLists) return false; @@ -3056,23 +3129,26 @@ void QMetaObject::connectSlotsByName(QObject *o) int len = objName.length(); if (!len || qstrncmp(slot + 3, objName.data(), len) || slot[len+3] != '_') continue; - const QMetaObject *smo = co->metaObject(); - int sigIndex = smo->indexOfMethod(slot + len + 4); + int sigIndex = co->d_func()->signalIndex(slot + len + 4); if (sigIndex < 0) { // search for compatible signals + const QMetaObject *smo = co->metaObject(); int slotlen = qstrlen(slot + len + 4) - 1; for (int k = 0; k < co->metaObject()->methodCount(); ++k) { - if (smo->method(k).methodType() != QMetaMethod::Signal) + QMetaMethod method = smo->method(k); + if (method.methodType() != QMetaMethod::Signal) continue; - if (!qstrncmp(smo->method(k).signature(), slot + len + 4, slotlen)) { - sigIndex = k; + if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) { + int signalOffset, methodOffset; + computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset); + sigIndex = k + - methodOffset + signalOffset; break; } } } if (sigIndex < 0) continue; - if (QMetaObject::connect(co, sigIndex, o, i)) { + if (QMetaObjectPrivate::connect(co, sigIndex, o, i)) { foundIt = true; break; } @@ -3142,15 +3218,43 @@ static void blocking_activate(QObject *sender, int signal, QObjectPrivate::Conne } /*!\internal + \obsolete. + Used to be called from QMetaObject::activate(QObject *, QMetaObject *, int, int, void **) */ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv) { + int offset = sender->metaObject()->methodOffset(); + activate(sender, from_signal_index - offset, to_signal_index - offset, argv); +} + +/*!\internal + */ +void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, + void **argv) +{ + int signalOffset; + int methodOffset; + computeOffsets(m, &signalOffset, &methodOffset); + + int signal_index = signalOffset + local_signal_index; + if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 + && !qt_signal_spy_callback_set.signal_begin_callback + && !qt_signal_spy_callback_set.signal_end_callback) { + uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + if ((sender->d_func()->connectedSignals[n] & m) == 0) + // nothing connected to these signals, and no spy + return; + } + if (sender->d_func()->blockSig) return; + int signal_absolute_index = methodOffset + local_signal_index; + void *empty_argv[] = { 0 }; if (qt_signal_spy_callback_set.signal_begin_callback != 0) { - qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index, + qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index, argv ? argv : empty_argv); } @@ -3160,26 +3264,20 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; if (!connectionLists) { if (qt_signal_spy_callback_set.signal_end_callback != 0) - qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index); + qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); return; } ++connectionLists->inUse; + if (signal_index >= connectionLists->count()) { + signal_index = -2; //for "all signals"; + } - // emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0 - for (int signal = from_signal_index; - (signal >= from_signal_index && signal <= to_signal_index) || (signal == -2); - (signal == to_signal_index ? signal = -2 : ++signal)) - { - if (signal >= connectionLists->count()) { - signal = to_signal_index; - continue; - } - - QObjectPrivate::Connection *c = connectionLists->at(signal).first; + do { + QObjectPrivate::Connection *c = connectionLists->at(signal_index).first; if (!c) continue; // We need to check against last here to ensure that signals added // during the signal emission are not emitted in this emission. - QObjectPrivate::Connection *last = connectionLists->at(signal).last; + QObjectPrivate::Connection *last = connectionLists->at(signal_index).last; do { if (!c->receiver) @@ -3193,17 +3291,17 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal && (currentThreadData != sender->d_func()->threadData || receiver->d_func()->threadData != sender->d_func()->threadData)) || (c->connectionType == Qt::QueuedConnection)) { - queued_activate(sender, signal, c, argv); + queued_activate(sender, signal_absolute_index, c, argv); continue; } else if (c->connectionType == Qt::BlockingQueuedConnection) { - blocking_activate(sender, signal, c, argv); + blocking_activate(sender, signal_absolute_index, c, argv); continue; } const int method = c->method; QObjectPrivate::Sender currentSender; currentSender.sender = sender; - currentSender.signal = signal < 0 ? from_signal_index : signal; + currentSender.signal = signal_absolute_index; currentSender.ref = 1; QObjectPrivate::Sender *previousSender = 0; if (currentThreadData == receiver->d_func()->threadData) @@ -3247,7 +3345,7 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal if (connectionLists->orphaned) break; - } + } while (signal_index >= 0 && (signal_index = -1)); //start over for -1 (all signal) --connectionLists->inUse; Q_ASSERT(connectionLists->inUse >= 0); @@ -3261,82 +3359,50 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal locker.unlock(); if (qt_signal_spy_callback_set.signal_end_callback != 0) - qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index); -} + qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); +} /*!\internal - */ + Obsolete. (signal_index comes from indexOfMethod()) +*/ void QMetaObject::activate(QObject *sender, int signal_index, void **argv) { - if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 - && !qt_signal_spy_callback_set.signal_begin_callback - && !qt_signal_spy_callback_set.signal_end_callback) { - uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); - if ((sender->d_func()->connectedSignals[n] & m) == 0) - // nothing connected to these signals, and no spy - return; - } - activate(sender, signal_index, signal_index, argv); -} - -/*!\internal - */ -void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, - void **argv) -{ - int signal_index = m->methodOffset() + local_signal_index; - if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 - && !qt_signal_spy_callback_set.signal_begin_callback - && !qt_signal_spy_callback_set.signal_end_callback) { - uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); - if ((sender->d_func()->connectedSignals[n] & m) == 0) - // nothing connected to these signals, and no spy - return; - } - activate(sender, signal_index, signal_index, argv); + const QMetaObject *mo = sender->metaObject(); + while (mo && mo->methodOffset() > signal_index) + mo = mo->superClass(); + activate(sender, signal_index - mo->methodOffset(), argv); } /*!\internal + Obsolete, called by moc generated code before Qt 4.6 for cloned signals + But since Qt 4.6, all clones are connected to their original */ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int from_local_signal_index, int to_local_signal_index, void **argv) { - Q_ASSERT(from_local_signal_index <= to_local_signal_index); - int offset = m->methodOffset(); - int from_signal_index = offset + from_local_signal_index; - int to_signal_index = offset + to_local_signal_index; + Q_UNUSED(to_local_signal_index); + Q_ASSERT(from_local_signal_index == QMetaObjectPrivate::originalClone(m, to_local_signal_index)); + activate(sender, m, from_local_signal_index, argv); +} - if (to_signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 - && !qt_signal_spy_callback_set.signal_begin_callback - && !qt_signal_spy_callback_set.signal_end_callback) { +/*! \internal + Returns the signal index used in the internal connectionLists vector. - uint n = (from_signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - uint m = 1 << (from_signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); - uint nt = (to_signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - uint mt = 1 << (to_signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); - bool connected = false; - quint32 *connectedSignals = sender->d_func()->connectedSignals; - for (uint i = 0; !connected && i < (sizeof sender->d_func()->connectedSignals - / sizeof sender->d_func()->connectedSignals[0]); ++i) { - uint mask = 0; - if (i > n) - mask = ~0u; - else if (i == n) - mask = ~(m -1); - if (i > nt) - mask = 0; - else if (i == nt) - mask &= (mt << 1) - 1; - connected = connectedSignals[i] & mask; - } - if (!connected) - // nothing connected to these signals, and no spy - return; - } - activate(sender, from_signal_index, to_signal_index, argv); + It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod + while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots. +*/ +int QObjectPrivate::signalIndex(const char *signalName) const +{ + Q_Q(const QObject); + const QMetaObject *base = q->metaObject(); + int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName); + if (relative_index < 0) + return relative_index; + relative_index = QMetaObjectPrivate::originalClone(base, relative_index); + int signalOffset, methodOffset; + computeOffsets(base, &signalOffset, &methodOffset); + return relative_index + signalOffset; } /*! \internal @@ -3344,14 +3410,16 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, Returns true if the signal with index \a signal_index from object \a sender is connected. Signals with indices above a certain range are always considered connected (see connectedSignals in QObjectPrivate). If a signal spy is installed, all signals are considered connected. + + \a signal_index must be the index returned by QObjectPrivate::signalIndex; */ -bool QMetaObject::isConnected(QObject *sender, int signal_index) { - if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 +bool QObjectPrivate::isSignalConnected(int signal_index) const { + if (signal_index < (int)sizeof(connectedSignals) * 8 && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { - uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); - uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); - if ((sender->d_func()->connectedSignals[n] & m) == 0) + uint n = (signal_index / (8 * sizeof connectedSignals[0])); + uint m = 1 << (signal_index - n * 8 * sizeof connectedSignals[0]); + if ((connectedSignals[n] & m) == 0) // nothing connected to these signals, and no spy return false; } @@ -3557,8 +3625,21 @@ void QObject::dumpObjectInfo() qDebug(" SIGNALS OUT"); if (d->connectionLists) { + int offset = 0; + int offsetToNextMetaObject = 0; for (int signal_index = 0; signal_index < d->connectionLists->count(); ++signal_index) { - const QMetaMethod signal = metaObject()->method(signal_index); + if (signal_index >= offsetToNextMetaObject) { + const QMetaObject *mo = metaObject(); + int signalOffset, methodOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + while (mo && signalOffset > signal_index) { + mo = mo->superClass(); + offsetToNextMetaObject = signalOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + } + offset = offset - signalOffset + methodOffset; + } + const QMetaMethod signal = metaObject()->method(signal_index + offset); qDebug(" signal: %s", signal.signature()); // receivers diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 49d8b63..9721ef8 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -169,6 +169,9 @@ public: return o->d_func(); } + int signalIndex(const char *signalName) const; + bool isSignalConnected(int signalIdx) const; + public: QString objectName; ExtraData *extraData; // extra data set by the user diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index befd596..421617a 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -330,12 +330,10 @@ struct Q_CORE_EXPORT QMetaObject static void connectSlotsByName(QObject *o); // internal index-based signal activation - static void activate(QObject *sender, int signal_index, void **argv); - static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); + static void activate(QObject *sender, int signal_index, void **argv); //obsolete + static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv); - static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); - - static bool isConnected(QObject *sender, int signal_index); + static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete // internal guarded pointers static void addGuard(QObject **ptr); diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 54d6073..533ccd7 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -2467,7 +2467,7 @@ double QVariant::toDouble(bool *ok) const */ float QVariant::toFloat(bool *ok) const { - return qNumVariantToHelper<float>(d, handler, ok, d.data.d); + return qNumVariantToHelper<float>(d, handler, ok, d.data.f); } /*! @@ -2484,7 +2484,7 @@ float QVariant::toFloat(bool *ok) const */ qreal QVariant::toReal(bool *ok) const { - return qNumVariantToHelper<qreal>(d, handler, ok, d.data.d); + return qNumVariantToHelper<qreal>(d, handler, ok, d.data.real); } /*! diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 79bd5b8..d6a704e 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -358,6 +358,7 @@ class Q_CORE_EXPORT QVariant bool b; double d; float f; + qreal real; qlonglong ll; qulonglong ull; QObject *o; diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 993c43b..c36899d 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -57,6 +57,7 @@ // to a pointer of the input type #include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> QT_BEGIN_NAMESPACE @@ -145,6 +146,8 @@ inline void v_clear(QVariant::Private *d, T* = 0) } +Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); + QT_END_NAMESPACE #endif // QVARIANT_P_H diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 2853b1a..defa7af 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -217,6 +217,8 @@ public: static const Handler *handler; }; +Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler(); + QT_END_NAMESPACE #endif diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index bebf141..a3053bb 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -832,7 +832,7 @@ struct QRegExpEngineKey } }; -bool operator==(const QRegExpEngineKey &key1, const QRegExpEngineKey &key2) +static bool operator==(const QRegExpEngineKey &key1, const QRegExpEngineKey &key2) { return key1.pattern == key2.pattern && key1.patternSyntax == key2.patternSyntax && key1.cs == key2.cs; diff --git a/src/dbus/qdbusmessage.cpp b/src/dbus/qdbusmessage.cpp index a676a01..851eaf0 100644 --- a/src/dbus/qdbusmessage.cpp +++ b/src/dbus/qdbusmessage.cpp @@ -707,7 +707,7 @@ QDBusMessage::MessageType QDBusMessage::type() const \sa QDBusConnection::send() */ #ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t) +static QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t) { switch (t) { diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 675fc0d..1d52182 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1010,7 +1010,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } // Resolve depth. - resolveDepth(parent ? parent->d_ptr->depth : -1); + invalidateDepthRecursively(); dirtySceneTransform = 1; // Restore the sub focus chain. @@ -4401,14 +4401,42 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore /*! \internal +*/ +int QGraphicsItemPrivate::depth() const +{ + if (itemDepth == -1) + const_cast<QGraphicsItemPrivate *>(this)->resolveDepth(); + + return itemDepth; +} - Resolves the stacking depth of this object and all its children. +/*! + \internal */ -void QGraphicsItemPrivate::resolveDepth(int parentDepth) +void QGraphicsItemPrivate::invalidateDepthRecursively() { - depth = parentDepth + 1; + if (itemDepth == -1) + return; + + itemDepth = -1; for (int i = 0; i < children.size(); ++i) - children.at(i)->d_ptr->resolveDepth(depth); + children.at(i)->d_ptr->invalidateDepthRecursively(); +} + +/*! + \internal + + Resolves the stacking depth of this object and all its ancestors. +*/ +void QGraphicsItemPrivate::resolveDepth() +{ + if (!parent) + itemDepth = 0; + else { + if (parent->d_ptr->itemDepth == -1) + parent->d_ptr->resolveDepth(); + itemDepth = parent->d_ptr->itemDepth + 1; + } } /*! @@ -5578,8 +5606,8 @@ QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) con return const_cast<QGraphicsItem *>(this); const QGraphicsItem *thisw = this; const QGraphicsItem *otherw = other; - int thisDepth = d_ptr->depth; - int otherDepth = other->d_ptr->depth; + int thisDepth = d_ptr->depth(); + int otherDepth = other->d_ptr->depth(); while (thisDepth > otherDepth) { thisw = thisw->d_ptr->parent; --thisDepth; @@ -6589,7 +6617,7 @@ void QGraphicsItem::prepareGeometryChange() // if someone is connected to the changed signal or the scene has no views. // Note that this has to be done *after* markDirty to ensure that // _q_processDirtyItems is called before _q_emitUpdated. - if ((scenePrivate->connectedSignals[0] & scenePrivate->changedSignalMask) + if (scenePrivate->isSignalConnected(scenePrivate->changedSignalIndex) || scenePrivate->views.isEmpty()) { if (d_ptr->hasTranslateOnlySceneTransform()) { d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 43d690f..ae7feb2 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -123,7 +123,7 @@ public: transformData(0), index(-1), siblingIndex(-1), - depth(0), + itemDepth(-1), focusProxy(0), subFocusItem(0), imHints(Qt::ImhNone), @@ -209,7 +209,9 @@ public: void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; - void resolveDepth(int parentDepth); + int depth() const; + void invalidateDepthRecursively(); + void resolveDepth(); void addChild(QGraphicsItem *child); void removeChild(QGraphicsItem *child); void setParentItemHelper(QGraphicsItem *parent); @@ -419,7 +421,7 @@ public: QTransform sceneTransform; int index; int siblingIndex; - int depth; + int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; QList<QGraphicsItem **> focusProxyRefs; QGraphicsItem *subFocusItem; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 3f13a86..7fa565d 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -265,12 +265,13 @@ static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraph hover->setAccepted(mouseEvent->isAccepted()); } +int QGraphicsScenePrivate::changedSignalIndex; + /*! \internal */ QGraphicsScenePrivate::QGraphicsScenePrivate() - : changedSignalMask(0), - indexMethod(QGraphicsScene::BspTreeIndex), + : indexMethod(QGraphicsScene::BspTreeIndex), index(0), lastItemCount(0), hasSceneRect(false), @@ -311,7 +312,9 @@ void QGraphicsScenePrivate::init() index = new QGraphicsSceneBspTreeIndex(q); // Keep this index so we can check for connected slots later on. - changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)")); + if (!changedSignalIndex) { + changedSignalIndex = signalIndex("changed(QList<QRectF>)"); + } qApp->d_func()->scene_list.append(q); q->update(); } @@ -343,7 +346,7 @@ void QGraphicsScenePrivate::_q_emitUpdated() // the optimization that items send updates directly to the views, but it // needs to happen in order to keep compatibility with the behavior from // Qt 4.4 and backward. - if (connectedSignals[0] & changedSignalMask) { + if (isSignalConnected(changedSignalIndex)) { for (int i = 0; i < views.size(); ++i) { QGraphicsView *view = views.at(i); if (!view->d_func()->connectedToScene) { @@ -2894,7 +2897,7 @@ void QGraphicsScene::update(const QRectF &rect) // Check if anyone's connected; if not, we can send updates directly to // the views. Otherwise or if there are no views, use old behavior. - bool directUpdates = !(d->connectedSignals[0] & d->changedSignalMask) && !d->views.isEmpty(); + bool directUpdates = !(d->isSignalConnected(d->changedSignalIndex)) && !d->views.isEmpty(); if (rect.isNull()) { d->updateAll = true; d->updatedRects.clear(); @@ -4473,7 +4476,7 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b if (removingItemFromScene) { // Note that this function can be called from the item's destructor, so // do NOT call any virtual functions on it within this block. - if ((connectedSignals[0] & changedSignalMask) || views.isEmpty()) { + if (isSignalConnected(changedSignalIndex) || views.isEmpty()) { // This block of code is kept for compatibility. Since 4.5, by default // QGraphicsView does not connect the signal and we use the below // method of delivering updates. @@ -4619,7 +4622,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process item. if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { - const bool useCompatUpdate = views.isEmpty() || (connectedSignals[0] & changedSignalMask); + const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex); const QRectF itemBoundingRect = adjustedItemBoundingRect(item); if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 685f534..f1ddb5a 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -87,7 +87,7 @@ public: static QGraphicsScenePrivate *get(QGraphicsScene *q); - quint32 changedSignalMask; + static int changedSignalIndex; QGraphicsScene::ItemIndexMethod indexMethod; QGraphicsSceneIndex *index; diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index d68183c..a80cdca 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -419,8 +419,8 @@ bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGra // Find common ancestor, and each item's ancestor closest to the common // ancestor. - int item1Depth = d1->depth; - int item2Depth = d2->depth; + int item1Depth = d1->depth(); + int item2Depth = d2->depth(); const QGraphicsItem *p = item1; const QGraphicsItem *t1 = item1; while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index ccf8457..19701b4 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -81,14 +81,14 @@ static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data -QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) +static QDataStream &operator>>(QDataStream &s, BMP_FILEHDR &bf) { // read file header s.readRawData(bf.bfType, 2); s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits; return s; } -QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) +static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) { // write file header s.writeRawData(bf.bfType, 2); s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits; @@ -106,7 +106,7 @@ const int BMP_RLE4 = 2; // run-length encoded, 4 const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields -QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) +static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) { s >> bi.biSize; if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) { @@ -128,7 +128,7 @@ QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) return s; } -QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) +static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi) { s << bi.biSize; s << bi.biWidth << bi.biHeight; diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index b2944ef..5d5c211 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -85,6 +85,7 @@ struct QIconDirInfo class QIconLoaderEngineEntry { public: + virtual ~QIconLoaderEngineEntry() {} virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) = 0; diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 574d845..ccf6ac9 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -101,6 +101,10 @@ extern bool qt_wince_is_pocket_pc(); //qguifunctions_wince.cpp #include "qdatetime.h" +#ifdef QT_MAC_USE_COCOA +#include <private/qt_cocoa_helpers_mac_p.h> +#endif + //#define ALIEN_DEBUG static void initResources() @@ -3477,6 +3481,15 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); +#ifdef QT_MAC_USE_COCOA + // We use native NSCursor stacks in Cocoa. The currentCursor is the + // top of this stack. So to avoid flickering of cursor, we have to + // change the cusor instead of pop-ing the existing OverrideCursor + // and pushing the new one. + qApp->d_func()->cursor_list.prepend(cursor); + qt_cocoaChangeOverrideCursor(cursor); + return; +#endif setOverrideCursor(cursor); } #endif diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 7ac0d89..5a0209d 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -667,7 +667,15 @@ extern "C" { - (void)mouseMoved:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + // We always enable mouse tracking for all QCocoaView-s. In cases where we have + // child views, we will receive mouseMoved for both parent & the child (if + // mouse is over the child). We need to ignore the parent mouseMoved in such + // cases. + NSPoint windowPoint = [theEvent locationInWindow]; + NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; + if (candidateView && candidateView == self) { + qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + } } - (void)mouseDown:(NSEvent *)theEvent diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp index a4042c1..8f92fea 100644 --- a/src/gui/kernel/qdnd_x11.cpp +++ b/src/gui/kernel/qdnd_x11.cpp @@ -1073,12 +1073,14 @@ void qt_xdnd_send_leave() if (!qt_xdnd_current_target) return; + QDragManager *manager = QDragManager::self(); + XClientMessageEvent leave; leave.type = ClientMessage; leave.window = qt_xdnd_current_target; leave.format = 32; leave.message_type = ATOM(XdndLeave); - leave.data.l[0] = qt_xdnd_dragsource_xid; + leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); leave.data.l[1] = 0; // flags leave.data.l[2] = 0; // x, y leave.data.l[3] = 0; // w, h @@ -1094,8 +1096,8 @@ void qt_xdnd_send_leave() else XSendEvent(X11->display, qt_xdnd_current_proxy_target, False, NoEventMask, (XEvent*)&leave); + // reset the drag manager state - QDragManager *manager = QDragManager::self(); manager->willDrop = false; if (global_accepted_action != Qt::IgnoreAction) manager->emitActionChanged(Qt::IgnoreAction); diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index c965bac..0027deb 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -99,11 +99,11 @@ struct QShortcutEntry QObject *owner; }; -#ifndef QT_NO_DEBUG_STREAM +#if 0 //ndef QT_NO_DEBUG_STREAM /*! \internal QDebug operator<< for easy debug output of the shortcut entries. */ -QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { +static QDebug &operator<<(QDebug &dbg, const QShortcutEntry *se) { if (!se) return dbg << "QShortcutEntry(0x0)"; dbg.nospace() diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 7596802..a9b8970 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -1238,4 +1238,12 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } +#ifdef QT_MAC_USE_COCOA +void qt_cocoaChangeOverrideCursor(const QCursor &cursor) +{ + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; +} +#endif + QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 24d7096..932abef 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -133,6 +133,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); +void qt_cocoaChangeOverrideCursor(const QCursor &cursor); #endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index 51f40e3..6a5ea49 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -642,7 +642,7 @@ GtkStyle* QGtk::gtkStyle(const QString &path) return 0; } -static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *pspec, gpointer user_data) +static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) { GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL); diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index bf2d58a..397e0b5 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -65,19 +65,37 @@ QT_BEGIN_NAMESPACE -const int QHttpNetworkConnectionPrivate::channelCount = 6; +const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6; + +// the maximum amount of requests that might be pipelined into a socket +// from what was suggested, 3 seems to be OK +const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3; + QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt) : hostName(hostName), port(port), encrypt(encrypt), + channelCount(defaultChannelCount), pendingAuthSignal(false), pendingProxyAuthSignal(false) #ifndef QT_NO_NETWORKPROXY , networkProxy(QNetworkProxy::NoProxy) #endif +{ + channels = new QHttpNetworkConnectionChannel[channelCount]; +} +QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt) +: hostName(hostName), port(port), encrypt(encrypt), + channelCount(channelCount), + pendingAuthSignal(false), pendingProxyAuthSignal(false) +#ifndef QT_NO_NETWORKPROXY + , networkProxy(QNetworkProxy::NoProxy) +#endif { channels = new QHttpNetworkConnectionChannel[channelCount]; } + + QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() { for (int i = 0; i < channelCount; ++i) { @@ -139,17 +157,6 @@ qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const return reply.d_func()->responseData.sizeNextBlock(); } -qint64 QHttpNetworkConnectionPrivate::compressedBytesAvailable(const QHttpNetworkReply &reply) const -{ - return reply.d_func()->compressedData.size(); -} - -void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply) -{ - reply->d_func()->compressedData.clear(); - reply->d_func()->responseData.clear(); -} - void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) { QHttpNetworkRequest &request = messagePair.first; @@ -223,231 +230,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) reply->d_func()->requestIsPrepared = true; } -bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket) -{ - // make sure that this socket is in a connected state, if not initiate - // connection to the host. - if (socket->state() != QAbstractSocket::ConnectedState) { - // connect to the host if not already connected. - int index = indexOf(socket); - // resend this request after we receive the disconnected signal - if (socket->state() == QAbstractSocket::ClosingState) { - channels[index].resendCurrent = true; - return false; - } - channels[index].state = QHttpNetworkConnectionChannel::ConnectingState; - channels[index].pendingEncrypt = encrypt; - - // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done" - // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the - // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not - // check the "phase" for generating the Authorization header. NTLM authentication is a two stage - // process & needs the "phase". To make sure the QAuthenticator uses the current username/password - // the phase is reset to Start. - QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[index].authenticator); - if (priv && priv->phase == QAuthenticatorPrivate::Done) - priv->phase = QAuthenticatorPrivate::Start; - priv = QAuthenticatorPrivate::getPrivate(channels[index].proxyAuthenticator); - if (priv && priv->phase == QAuthenticatorPrivate::Done) - priv->phase = QAuthenticatorPrivate::Start; - - QString connectHost = hostName; - qint16 connectPort = port; - -#ifndef QT_NO_NETWORKPROXY - // HTTPS always use transparent proxy. - if (networkProxy.type() != QNetworkProxy::NoProxy && !encrypt) { - connectHost = networkProxy.hostName(); - connectPort = networkProxy.port(); - } -#endif - if (encrypt) { -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - sslSocket->connectToHostEncrypted(connectHost, connectPort); - if (channels[index].ignoreAllSslErrors) - sslSocket->ignoreSslErrors(); - sslSocket->ignoreSslErrors(channels[index].ignoreSslErrorsList); -#else - emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError); -#endif - } else { - socket->connectToHost(connectHost, connectPort); - } - return false; - } - return true; -} - -bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) -{ - Q_Q(QHttpNetworkConnection); - int i = indexOf(socket); - switch (channels[i].state) { - case QHttpNetworkConnectionChannel::IdleState: { // write the header - if (!ensureConnection(socket)) { - // wait for the connection (and encryption) to be done - // sendRequest will be called again from either - // _q_connected or _q_encrypted - return false; - } - channels[i].written = 0; // excluding the header - channels[i].bytesTotal = 0; - if (channels[i].reply) { - channels[i].reply->d_func()->clear(); - channels[i].reply->d_func()->connection = q; - channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress; - } - channels[i].state = QHttpNetworkConnectionChannel::WritingState; - channels[i].pendingEncrypt = false; - // if the url contains authentication parameters, use the new ones - // both channels will use the new authentication parameters - if (!channels[i].request.url().userInfo().isEmpty()) { - QUrl url = channels[i].request.url(); - QAuthenticator &auth = channels[i].authenticator; - if (url.userName() != auth.user() - || (!url.password().isEmpty() && url.password() != auth.password())) { - auth.setUser(url.userName()); - auth.setPassword(url.password()); - copyCredentials(i, &auth, false); - } - // clear the userinfo, since we use the same request for resending - // userinfo in url can conflict with the one in the authenticator - url.setUserInfo(QString()); - channels[i].request.setUrl(url); - } - createAuthorization(socket, channels[i].request); -#ifndef QT_NO_NETWORKPROXY - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - (networkProxy.type() != QNetworkProxy::NoProxy)); -#else - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - false); -#endif - socket->write(header); - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - // connect the signals so this function gets called again - QObject::connect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); - - channels[i].bytesTotal = channels[i].request.contentLength(); - } else { - socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets! - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; - break; - } - // write the initial chunk together with the headers - // fall through - } - case QHttpNetworkConnectionChannel::WritingState: - { - // write the data - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (!uploadByteDevice || channels[i].bytesTotal == channels[i].written) { - if (uploadByteDevice) - emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response - sendRequest(socket); - break; - } - - // only feed the QTcpSocket buffer when there is less than 32 kB in it - const qint64 socketBufferFill = 32*1024; - const qint64 socketWriteMaxSize = 16*1024; - - -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - // if it is really an ssl socket, check more than just bytesToWrite() - while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) - <= socketBufferFill && channels[i].bytesTotal != channels[i].written) -#else - while (socket->bytesToWrite() <= socketBufferFill - && channels[i].bytesTotal != channels[i].written) -#endif - { - // get pointer to upload data - qint64 currentReadSize; - qint64 desiredReadSize = qMin(socketWriteMaxSize, channels[i].bytesTotal - channels[i].written); - const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); - - if (currentReadSize == -1) { - // premature eof happened - emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); - return false; - break; - } else if (readPointer == 0 || currentReadSize == 0) { - // nothing to read currently, break the loop - break; - } else { - qint64 currentWriteSize = socket->write(readPointer, currentReadSize); - if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { - // socket broke down - emitReplyError(socket, channels[i].reply, QNetworkReply::UnknownNetworkError); - return false; - } else { - channels[i].written += currentWriteSize; - uploadByteDevice->advanceReadPointer(currentWriteSize); - - emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); - - if (channels[i].written == channels[i].bytesTotal) { - // make sure this function is called once again - channels[i].state = QHttpNetworkConnectionChannel::WaitingState; - sendRequest(socket); - break; - } - } - } - } - break; - } - - case QHttpNetworkConnectionChannel::WaitingState: - { - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); - } - // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called - // this is needed if the sends an reply before we have finished sending the request. In that - // case receiveReply had been called before but ignored the server reply - receiveReply(socket, channels[i].reply); - break; - } - case QHttpNetworkConnectionChannel::ReadingState: - case QHttpNetworkConnectionChannel::Wait4AuthState: - // ignore _q_bytesWritten in these states - // fall through - default: - break; - } - return true; -} - -bool QHttpNetworkConnectionPrivate::shouldEmitSignals(QHttpNetworkReply *reply) -{ - // for 401 & 407 don't emit the data signals. Content along with these - // responses are send only if the authentication fails. - return (reply && reply->d_func()->statusCode != 401 && reply->d_func()->statusCode != 407); -} - -bool QHttpNetworkConnectionPrivate::expectContent(QHttpNetworkReply *reply) -{ - // check whether we can expect content after the headers (rfc 2616, sec4.4) - if (!reply) - return false; - if ((reply->d_func()->statusCode >= 100 && reply->d_func()->statusCode < 200) - || reply->d_func()->statusCode == 204 || reply->d_func()->statusCode == 304) - return false; - if (reply->d_func()->request.operation() == QHttpNetworkRequest::Head) - return !shouldEmitSignals(reply); - if (reply->d_func()->contentLength() == 0) - return false; - return true; -} void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, @@ -460,271 +244,13 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, emit reply->finishedWithError(errorCode, reply->d_func()->errorString); int i = indexOf(socket); // remove the corrupt data if any - eraseData(channels[i].reply); - closeChannel(i); + reply->d_func()->eraseData(); + channels[i].close(); // send the next request QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); } } -#ifndef QT_NO_COMPRESS -bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete) -{ - Q_ASSERT(socket); - Q_ASSERT(reply); - - qint64 total = compressedBytesAvailable(*reply); - if (total >= CHUNK || dataComplete) { - int i = indexOf(socket); - // uncompress the data - QByteArray content, inflated; - content = reply->d_func()->compressedData; - reply->d_func()->compressedData.clear(); - - int ret = Z_OK; - if (content.size()) - ret = reply->d_func()->gunzipBodyPartially(content, inflated); - int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK; - if (ret >= retCheck) { - if (inflated.size()) { - reply->d_func()->totalProgress += inflated.size(); - reply->d_func()->appendUncompressedReplyData(inflated); - if (shouldEmitSignals(reply)) { - // important: At the point of this readyRead(), inflated must be cleared, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return true; - emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); - // make sure that the reply is valid - if (channels[i].reply != reply) - return true; - - } - } - } else { - emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); - return false; - } - } - return true; -} -#endif - -void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ - Q_ASSERT(socket); - - Q_Q(QHttpNetworkConnection); - qint64 bytes = 0; - QAbstractSocket::SocketState state = socket->state(); - int i = indexOf(socket); - - // connection might be closed to signal the end of data - if (state == QAbstractSocket::UnconnectedState) { - if (!socket->bytesAvailable()) { - if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { - reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - } else { - // try to reconnect/resend before sending an error. - if (channels[i].reconnectAttempts-- > 0) { - resendCurrentRequest(socket); - } else if (reply) { - reply->d_func()->errorString = errorDetail(QNetworkReply::RemoteHostClosedError, socket); - emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - } - } - } - - // read loop for the response - while (socket->bytesAvailable()) { - QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; - switch (state) { - case QHttpNetworkReplyPrivate::NothingDoneState: - case QHttpNetworkReplyPrivate::ReadingStatusState: { - qint64 statusBytes = reply->d_func()->readStatus(socket); - if (statusBytes == -1) { - // error reading the status, close the socket and emit error - socket->close(); - reply->d_func()->errorString = errorDetail(QNetworkReply::ProtocolFailure, socket); - emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - break; - } - bytes += statusBytes; - channels[i].lastStatus = reply->d_func()->statusCode; - break; - } - case QHttpNetworkReplyPrivate::ReadingHeaderState: - bytes += reply->d_func()->readHeader(socket); - if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { - if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { - // remove the Content-Length from header - reply->d_func()->removeAutoDecompressHeader(); - } else { - reply->d_func()->autoDecompress = false; - } - if (reply && reply->d_func()->statusCode == 100) { - reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; - break; // ignore - } - if (shouldEmitSignals(reply)) - emit reply->headerChanged(); - if (!expectContent(reply)) { - reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - return; - } - } - break; - case QHttpNetworkReplyPrivate::ReadingDataState: { - if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress - && reply->d_func()->bodyLength > 0) { - // bulk files like images should fulfill these properties and - // we can therefore save on memory copying - bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData); - reply->d_func()->totalProgress += bytes; - if (shouldEmitSignals(reply)) { - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - } - } - else - { - // use the traditional slower reading (for compressed encoding, chunked encoding, - // no content-length etc) - QByteDataBuffer byteDatas; - bytes = reply->d_func()->readBody(socket, &byteDatas); - if (bytes) { - if (reply->d_func()->autoDecompress) - reply->d_func()->appendCompressedReplyData(byteDatas); - else - reply->d_func()->appendUncompressedReplyData(byteDatas); - - if (!reply->d_func()->autoDecompress) { - reply->d_func()->totalProgress += bytes; - if (shouldEmitSignals(reply)) { - // important: At the point of this readyRead(), the byteDatas list must be empty, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); - // make sure that the reply is valid - if (channels[i].reply != reply) - return; - } - } -#ifndef QT_NO_COMPRESS - else if (!expand(socket, reply, false)) { // expand a chunk if possible - return; // ### expand failed - } -#endif - } - } - if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) - break; - // everything done, fall through - } - case QHttpNetworkReplyPrivate::AllDoneState: - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - allDone(socket, reply); - break; - default: - break; - } - } -} - -void QHttpNetworkConnectionPrivate::allDone(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ -#ifndef QT_NO_COMPRESS - // expand the whole data. - if (expectContent(reply) && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) - expand(socket, reply, true); // ### if expand returns false, its an error -#endif - // while handling 401 & 407, we might reset the status code, so save this. - bool emitFinished = shouldEmitSignals(reply); - handleStatus(socket, reply); - // ### at this point there should be no more data on the socket - // close if server requested - int i = indexOf(socket); - if (reply->d_func()->connectionCloseEnabled()) - closeChannel(i); - // queue the finished signal, this is required since we might send new requests from - // slot connected to it. The socket will not fire readyRead signal, if we are already - // in the slot connected to readyRead - if (emitFinished) - QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection); - // reset the reconnection attempts after we receive a complete reply. - // in case of failures, each channel will attempt two reconnects before emitting error. - channels[i].reconnectAttempts = 2; -} - -void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply) -{ - Q_ASSERT(socket); - Q_ASSERT(reply); - - Q_Q(QHttpNetworkConnection); - - int statusCode = reply->statusCode(); - bool resend = false; - - switch (statusCode) { - case 401: - case 407: - if (handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { - if (resend) { - int i = indexOf(socket); - - QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); - if (uploadByteDevice) { - if (uploadByteDevice->reset()) { - channels[i].written = 0; - } else { - emitReplyError(socket, reply, QNetworkReply::ContentReSendError); - break; - } - } - - eraseData(reply); - - // also use async _q_startNextRequest so we dont break with closed - // proxy or server connections.. - channels[i].resendCurrent = true; - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - } else { - int i = indexOf(socket); - emit channels[i].reply->headerChanged(); - emit channels[i].reply->readyRead(); - QNetworkReply::NetworkError errorCode = (statusCode == 407) - ? QNetworkReply::ProxyAuthenticationRequiredError - : QNetworkReply::AuthenticationRequiredError; - reply->d_func()->errorString = errorDetail(errorCode, socket); - emit q->error(errorCode, reply->d_func()->errorString); - emit channels[i].reply->finished(); - } - break; - default: - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } -} - void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy) { Q_ASSERT(auth); @@ -747,6 +273,7 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica } +// handles the authentication for one channel and eventually re-starts the other channels bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend) { @@ -786,8 +313,8 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket if (priv->phase == QAuthenticatorPrivate::Done) { if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) { // drop the request - eraseData(channels[i].reply); - closeChannel(i); + reply->d_func()->eraseData(); + channels[i].close(); channels[i].lastStatus = 0; channels[i].state = QHttpNetworkConnectionChannel::Wait4AuthState; return false; @@ -892,7 +419,24 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor return reply; } -void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socket) +void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair) +{ + Q_Q(QHttpNetworkConnection); + + QHttpNetworkRequest request = pair.first; + switch (request.priority()) { + case QHttpNetworkRequest::HighPriority: + highPriorityQueue.prepend(pair); + break; + case QHttpNetworkRequest::NormalPriority: + case QHttpNetworkRequest::LowPriority: + lowPriorityQueue.prepend(pair); + break; + } + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); +} + +void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket) { Q_ASSERT(socket); @@ -906,8 +450,9 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + // remove before sendRequest! else we might pipeline the same request again highPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } @@ -919,28 +464,113 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke prepareRequest(messagePair); channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + // remove before sendRequest! else we might pipeline the same request again lowPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } } -void QHttpNetworkConnectionPrivate::closeChannel(int channel) +// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel +void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket) { - channels[channel].close(); + // return fast if there is nothing to pipeline + if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) + return; + + int i = indexOf(socket); + + bool highPriorityQueueProcessingDone = false; + bool lowPriorityQueueProcessingDone = false; + + while (!highPriorityQueueProcessingDone && !lowPriorityQueueProcessingDone) { + // this loop runs once per request we intend to pipeline in. + + if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported) + return; + + // the current request that is in must already support pipelining + if (!channels[i].request.isPipeliningAllowed()) + return; + + // the current request must be a idempotent (right now we only check GET) + if (channels[i].request.operation() != QHttpNetworkRequest::Get) + return; + + // check if socket is connected + if (socket->state() != QAbstractSocket::ConnectedState) + return; + + // check for resendCurrent + if (channels[i].resendCurrent) + return; + + // we do not like authentication stuff + // ### make sure to be OK with this in later releases + if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty()) + return; + if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty()) + return; + + // check for pipeline length + if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) + return; + + // must be in ReadingState or WaitingState + if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState + || channels[i].state == QHttpNetworkConnectionChannel::ReadingState)) + return; + + highPriorityQueueProcessingDone = fillPipeline(highPriorityQueue, channels[i]); + // not finished with highPriorityQueue? then loop again + if (!highPriorityQueueProcessingDone) + continue; + // highPriorityQueue was processed, now deal with the lowPriorityQueue + lowPriorityQueueProcessingDone = fillPipeline(lowPriorityQueue, channels[i]); + } } -void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) +// returns true when the processing of a queue has been done +bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel) { - Q_Q(QHttpNetworkConnection); - Q_ASSERT(socket); - int i = indexOf(socket); - closeChannel(i); - channels[i].resendCurrent = true; - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + if (queue.isEmpty()) + return true; + + for (int i = 0; i < queue.length(); i++) { + HttpMessagePair messagePair = queue.at(i); + const QHttpNetworkRequest &request = messagePair.first; + + // we currently do not support pipelining if HTTP authentication is used + if (!request.url().userInfo().isEmpty()) + continue; + + // take only GET requests + if (request.operation() != QHttpNetworkRequest::Get) + continue; + + if (!request.isPipeliningAllowed()) + continue; + + // remove it from the queue + queue.takeAt(i); + // we modify the queue we iterate over here, but since we return from the function + // afterwards this is fine. + + // actually send it + if (!messagePair.second->d_func()->requestIsPrepared) + prepareRequest(messagePair); + channel.pipelineInto(messagePair); + + // return false because we processed something and need to process again + return false; + } + + // return true, the queue has been processed and not changed + return true; } + QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket) { Q_ASSERT(socket); @@ -992,7 +622,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) if (channels[i].reply == reply) { channels[i].reply = 0; if (reply->d_func()->connectionCloseEnabled()) - closeChannel(i); + channels[i].close(); QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); return; } @@ -1031,7 +661,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() channels[i].resendCurrent = false; channels[i].state = QHttpNetworkConnectionChannel::IdleState; if (channels[i].reply) - sendRequest(channels[i].socket); + channels[i].sendRequest(); } } QAbstractSocket *socket = 0; @@ -1043,20 +673,33 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() break; } } - if (!socket) - return; // this will be called after finishing current request. - unqueueAndSendRequest(socket); + + // this socket is free, + if (socket) + dequeueAndSendRequest(socket); + + // try to push more into all sockets + // ### FIXME we should move this to the beginning of the function + // as soon as QtWebkit is properly using the pipelining + // (e.g. not for XMLHttpRequest or the first page load) + // ### FIXME we should also divide the requests more even + // on the connected sockets + //tryToFillPipeline(socket); + // return fast if there is nothing to pipeline + if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) + return; + for (int j = 0; j < channelCount; j++) + fillPipeline(channels[j].socket); } void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() { // send the request using the idle socket for (int i = 0 ; i < channelCount; ++i) { - QAbstractSocket *socket = channels[i].socket; if (channels[i].state == QHttpNetworkConnectionChannel::Wait4AuthState) { channels[i].state = QHttpNetworkConnectionChannel::IdleState; if (channels[i].reply) - sendRequest(socket); + channels[i].sendRequest(); } } } @@ -1069,6 +712,13 @@ QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 d->init(); } +QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent) + : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent) +{ + Q_D(QHttpNetworkConnection); + d->init(); +} + QHttpNetworkConnection::~QHttpNetworkConnection() { } @@ -1215,6 +865,19 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int #endif //QT_NO_OPENSSL +#ifndef QT_NO_NETWORKPROXY +// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not +// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge +// e.g. it is for SOCKS proxies which require authentication. +void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth) +{ + Q_Q(QHttpNetworkConnection); + emit q->proxyAuthenticationRequired(proxy, auth, q); + int i = indexOf(chan->socket); + copyCredentials(i, auth, true); +} +#endif + QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 99dda2a..af764ed 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -89,6 +89,7 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject public: QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0); + QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0); ~QHttpNetworkConnection(); //The hostname to which this is connected to. @@ -153,7 +154,11 @@ class QHttpNetworkConnectionPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QHttpNetworkConnection) public: + static const int defaultChannelCount; + static const int defaultPipelineLength; + QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); + QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); @@ -166,12 +171,13 @@ public: bool isSocketReading(QAbstractSocket *socket) const; QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request); - void unqueueAndSendRequest(QAbstractSocket *socket); + void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke + void dequeueAndSendRequest(QAbstractSocket *socket); void prepareRequest(HttpMessagePair &request); - bool sendRequest(QAbstractSocket *socket); - void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply); - void resendCurrentRequest(QAbstractSocket *socket); - void closeChannel(int channel); + + void fillPipeline(QAbstractSocket *socket); + bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel); + void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); // private slots @@ -179,9 +185,9 @@ public: void _q_restartAuthPendingRequests(); // send the currently blocked request void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); - bool ensureConnection(QAbstractSocket *socket); + QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket); - void eraseData(QHttpNetworkReply *reply); + #ifndef QT_NO_COMPRESS bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete); #endif @@ -191,7 +197,7 @@ public: quint16 port; bool encrypt; - static const int channelCount; + const int channelCount; QHttpNetworkConnectionChannel *channels; // parallel connections to the server bool pendingAuthSignal; // there is an incomplete authentication signal @@ -199,14 +205,11 @@ public: qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; - qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const; + void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode); bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend); - void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply); - void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply); - inline bool shouldEmitSignals(QHttpNetworkReply *reply); - inline bool expectContent(QHttpNetworkReply *reply); + #ifndef QT_NO_OPENSSL QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const; @@ -214,6 +217,7 @@ public: #ifndef QT_NO_NETWORKPROXY QNetworkProxy networkProxy; + void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth); #endif //The request queues diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 5ffab76..37386ea 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -41,6 +41,7 @@ #include "qhttpnetworkconnection_p.h" #include "qhttpnetworkconnectionchannel_p.h" +#include "private/qnoncontiguousbytedevice_p.h" #include <qpair.h> #include <qdebug.h> @@ -113,61 +114,644 @@ void QHttpNetworkConnectionChannel::close() } +bool QHttpNetworkConnectionChannel::sendRequest() +{ + switch (state) { + case QHttpNetworkConnectionChannel::IdleState: { // write the header + if (!ensureConnection()) { + // wait for the connection (and encryption) to be done + // sendRequest will be called again from either + // _q_connected or _q_encrypted + return false; + } + written = 0; // excluding the header + bytesTotal = 0; + if (reply) { + reply->d_func()->clear(); + reply->d_func()->connection = connection; + reply->d_func()->autoDecompress = request.d->autoDecompress; + } + state = QHttpNetworkConnectionChannel::WritingState; + pendingEncrypt = false; + // if the url contains authentication parameters, use the new ones + // both channels will use the new authentication parameters + if (!request.url().userInfo().isEmpty()) { + QUrl url = request.url(); + QAuthenticator &auth = authenticator; + if (url.userName() != auth.user() + || (!url.password().isEmpty() && url.password() != auth.password())) { + auth.setUser(url.userName()); + auth.setPassword(url.password()); + connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); + } + // clear the userinfo, since we use the same request for resending + // userinfo in url can conflict with the one in the authenticator + url.setUserInfo(QString()); + request.setUrl(url); + } + connection->d_func()->createAuthorization(socket, request); +#ifndef QT_NO_NETWORKPROXY + QByteArray header = QHttpNetworkRequestPrivate::header(request, + (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); +#else + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); +#endif + socket->write(header); + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + // connect the signals so this function gets called again + QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead())); + + bytesTotal = request.contentLength(); + } else { + state = QHttpNetworkConnectionChannel::WaitingState; + sendRequest(); + break; + } + // write the initial chunk together with the headers + // fall through + } + case QHttpNetworkConnectionChannel::WritingState: + { + // write the data + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (!uploadByteDevice || bytesTotal == written) { + if (uploadByteDevice) + emit reply->dataSendProgress(written, bytesTotal); + state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response + sendRequest(); + break; + } + + // only feed the QTcpSocket buffer when there is less than 32 kB in it + const qint64 socketBufferFill = 32*1024; + const qint64 socketWriteMaxSize = 16*1024; + + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + // if it is really an ssl socket, check more than just bytesToWrite() + while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) + <= socketBufferFill && bytesTotal != written) +#else + while (socket->bytesToWrite() <= socketBufferFill + && bytesTotal != written) +#endif + { + // get pointer to upload data + qint64 currentReadSize; + qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written); + const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize); + + if (currentReadSize == -1) { + // premature eof happened + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); + return false; + break; + } else if (readPointer == 0 || currentReadSize == 0) { + // nothing to read currently, break the loop + break; + } else { + qint64 currentWriteSize = socket->write(readPointer, currentReadSize); + if (currentWriteSize == -1 || currentWriteSize != currentReadSize) { + // socket broke down + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError); + return false; + } else { + written += currentWriteSize; + uploadByteDevice->advanceReadPointer(currentWriteSize); + + emit reply->dataSendProgress(written, bytesTotal); + + if (written == bytesTotal) { + // make sure this function is called once again + state = QHttpNetworkConnectionChannel::WaitingState; + sendRequest(); + break; + } + } + } + } + break; + } + + case QHttpNetworkConnectionChannel::WaitingState: + { + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead())); + } + + // HTTP pipelining + connection->d_func()->fillPipeline(socket); + socket->flush(); + + // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called + // this is needed if the sends an reply before we have finished sending the request. In that + // case receiveReply had been called before but ignored the server reply + receiveReply(); + break; + } + case QHttpNetworkConnectionChannel::ReadingState: + case QHttpNetworkConnectionChannel::Wait4AuthState: + // ignore _q_bytesWritten in these states + // fall through + default: + break; + } + return true; +} + + +void QHttpNetworkConnectionChannel::receiveReply() +{ + Q_ASSERT(socket); + + qint64 bytes = 0; + QAbstractSocket::SocketState socketState = socket->state(); + + // connection might be closed to signal the end of data + if (socketState == QAbstractSocket::UnconnectedState) { + if (!socket->bytesAvailable()) { + if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + } else { + // try to reconnect/resend before sending an error. + if (reconnectAttempts-- > 0) { + closeAndResendCurrentRequest(); + } else if (reply) { + reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); + emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + } + } + } + + // read loop for the response + while (socket->bytesAvailable()) { + QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; + switch (state) { + case QHttpNetworkReplyPrivate::NothingDoneState: + case QHttpNetworkReplyPrivate::ReadingStatusState: { + eatWhitespace(); + qint64 statusBytes = reply->d_func()->readStatus(socket); + if (statusBytes == -1 && reconnectAttempts <= 0) { + // too many errors reading/receiving/parsing the status, close the socket and emit error + close(); + reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket); + emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + break; + } else if (statusBytes == -1) { + reconnectAttempts--; + reply->d_func()->clear(); + closeAndResendCurrentRequest(); + break; + } + bytes += statusBytes; + lastStatus = reply->d_func()->statusCode; + break; + } + case QHttpNetworkReplyPrivate::ReadingHeaderState: + bytes += reply->d_func()->readHeader(socket); + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { + // remove the Content-Length from header + reply->d_func()->removeAutoDecompressHeader(); + } else { + reply->d_func()->autoDecompress = false; + } + if (reply && reply->d_func()->statusCode == 100) { + reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; + break; // ignore + } + if (reply->d_func()->shouldEmitSignals()) + emit reply->headerChanged(); + if (!reply->d_func()->expectContent()) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + return; + } + } + break; + case QHttpNetworkReplyPrivate::ReadingDataState: { + if (!reply->d_func()->isChunked() && !reply->d_func()->autoDecompress + && reply->d_func()->bodyLength > 0) { + // bulk files like images should fulfill these properties and + // we can therefore save on memory copying + bytes = reply->d_func()->readBodyFast(socket, &reply->d_func()->responseData); + reply->d_func()->totalProgress += bytes; + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + } + } + else + { + // use the traditional slower reading (for compressed encoding, chunked encoding, + // no content-length etc) + QByteDataBuffer byteDatas; + bytes = reply->d_func()->readBody(socket, &byteDatas); + if (bytes) { + if (reply->d_func()->autoDecompress) + reply->d_func()->appendCompressedReplyData(byteDatas); + else + reply->d_func()->appendUncompressedReplyData(byteDatas); + + if (!reply->d_func()->autoDecompress) { + reply->d_func()->totalProgress += bytes; + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + // important: At the point of this readyRead(), the byteDatas list must be empty, + // else implicit sharing will trigger memcpy when the user is reading data! + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); + // make sure that the reply is valid + if (replyPointer.isNull()) + return; + } + } +#ifndef QT_NO_COMPRESS + else if (!expand(false)) { // expand a chunk if possible + return; // ### expand failed + } +#endif + } + } + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) + break; + // everything done, fall through + } + case QHttpNetworkReplyPrivate::AllDoneState: + this->state = QHttpNetworkConnectionChannel::IdleState; + allDone(); + break; + default: + break; + } + } +} + +bool QHttpNetworkConnectionChannel::ensureConnection() +{ + // make sure that this socket is in a connected state, if not initiate + // connection to the host. + if (socket->state() != QAbstractSocket::ConnectedState) { + // connect to the host if not already connected. + // resend this request after we receive the disconnected signal + if (socket->state() == QAbstractSocket::ClosingState) { + resendCurrent = true; + return false; + } + state = QHttpNetworkConnectionChannel::ConnectingState; + pendingEncrypt = connection->d_func()->encrypt; + + // reset state + pipeliningSupported = PipeliningSupportUnknown; + + // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done" + // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the + // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not + // check the "phase" for generating the Authorization header. NTLM authentication is a two stage + // process & needs the "phase". To make sure the QAuthenticator uses the current username/password + // the phase is reset to Start. + QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + + QString connectHost = connection->d_func()->hostName; + qint16 connectPort = connection->d_func()->port; + +#ifndef QT_NO_NETWORKPROXY + // HTTPS always use transparent proxy. + if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) { + connectHost = connection->d_func()->networkProxy.hostName(); + connectPort = connection->d_func()->networkProxy.port(); + } +#endif + if (connection->d_func()->encrypt) { +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + sslSocket->connectToHostEncrypted(connectHost, connectPort); + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); + sslSocket->ignoreSslErrors(ignoreSslErrorsList); +#else + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); +#endif + } else { + socket->connectToHost(connectHost, connectPort); + } + return false; + } + return true; +} + + +#ifndef QT_NO_COMPRESS +bool QHttpNetworkConnectionChannel::expand(bool dataComplete) +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + qint64 total = reply->d_func()->compressedData.size(); + if (total >= CHUNK || dataComplete) { + // uncompress the data + QByteArray content, inflated; + content = reply->d_func()->compressedData; + reply->d_func()->compressedData.clear(); + + int ret = Z_OK; + if (content.size()) + ret = reply->d_func()->gunzipBodyPartially(content, inflated); + int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK; + if (ret >= retCheck) { + if (inflated.size()) { + reply->d_func()->totalProgress += inflated.size(); + reply->d_func()->appendUncompressedReplyData(inflated); + if (reply->d_func()->shouldEmitSignals()) { + QPointer<QHttpNetworkReply> replyPointer = reply; + // important: At the point of this readyRead(), inflated must be cleared, + // else implicit sharing will trigger memcpy when the user is reading data! + emit reply->readyRead(); + // make sure that the reply is valid + if (replyPointer.isNull()) + return true; + emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); + // make sure that the reply is valid + if (replyPointer.isNull()) + return true; + + } + } + } else { + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); + return false; + } + } + return true; +} +#endif + + +void QHttpNetworkConnectionChannel::allDone() +{ +#ifndef QT_NO_COMPRESS + // expand the whole data. + if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) + expand(true); // ### if expand returns false, its an error +#endif + // while handling 401 & 407, we might reset the status code, so save this. + bool emitFinished = reply->d_func()->shouldEmitSignals(); + handleStatus(); + // ### at this point there should be no more data on the socket + // close if server requested + if (reply->d_func()->connectionCloseEnabled()) + close(); + // queue the finished signal, this is required since we might send new requests from + // slot connected to it. The socket will not fire readyRead signal, if we are already + // in the slot connected to readyRead + if (emitFinished) + QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection); + // reset the reconnection attempts after we receive a complete reply. + // in case of failures, each channel will attempt two reconnects before emitting error. + reconnectAttempts = 2; + + detectPipeliningSupport(); + + // move next from pipeline to current request + if (!alreadyPipelinedRequests.isEmpty()) { + if (resendCurrent || reply->d_func()->connectionCloseEnabled() || socket->state() != QAbstractSocket::ConnectedState) { + // move the pipelined ones back to the main queue + requeueCurrentlyPipelinedRequests(); + } else { + // there were requests pipelined in and we can continue + HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst(); + + request = messagePair.first; + reply = messagePair.second; + state = QHttpNetworkConnectionChannel::ReadingState; + resendCurrent = false; + + written = 0; // message body, excluding the header, irrelevant here + bytesTotal = 0; // message body total, excluding the header, irrelevant here + + // pipeline even more + connection->d_func()->fillPipeline(socket); + + // continue reading + receiveReply(); + } + } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) { + eatWhitespace(); + // this is weird. we had nothing pipelined but still bytes available. better close it. + if (socket->bytesAvailable() > 0) + close(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } else if (alreadyPipelinedRequests.isEmpty()) { + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +void QHttpNetworkConnectionChannel::detectPipeliningSupport() +{ + // detect HTTP Pipelining support + QByteArray serverHeaderField = reply->headerField("Server"); + if ( + // check for broken servers in server reply header + // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining + (!serverHeaderField.contains("Microsoft-IIS/4.")) + && (!serverHeaderField.contains("Microsoft-IIS/5.")) + && (!serverHeaderField.contains("Netscape-Enterprise/3.")) + // check for HTTP/1.1 + && (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1) + // check for not having connection close + && (!reply->d_func()->connectionCloseEnabled()) + // check if it is still connected + && (socket->state() == QAbstractSocket::ConnectedState) + ) { + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported; + } else { + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; + } +} + +// called when the connection broke and we need to queue some pipelined requests again +void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests() +{ + for (int i = 0; i < alreadyPipelinedRequests.length(); i++) + connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); + alreadyPipelinedRequests.clear(); + + close(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +} + +void QHttpNetworkConnectionChannel::eatWhitespace() +{ + char c; + while (socket->bytesAvailable()) { + if (socket->peek(&c, 1) != 1) + return; + + // read all whitespace and line endings + if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) { + socket->read(&c, 1); + continue; + } else { + break; + } + } +} + +void QHttpNetworkConnectionChannel::handleStatus() +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + int statusCode = reply->statusCode(); + bool resend = false; + + switch (statusCode) { + case 401: // auth required + case 407: // proxy auth required + if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) { + if (resend) { + QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); + if (uploadByteDevice) { + if (uploadByteDevice->reset()) { + written = 0; + } else { + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError); + break; + } + } + + reply->d_func()->eraseData(); + + if (alreadyPipelinedRequests.isEmpty()) { + // this does a re-send without closing the connection + resendCurrent = true; + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } else { + // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest + closeAndResendCurrentRequest(); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + } + } else { + emit reply->headerChanged(); + emit reply->readyRead(); + QNetworkReply::NetworkError errorCode = (statusCode == 407) + ? QNetworkReply::ProxyAuthenticationRequiredError + : QNetworkReply::AuthenticationRequiredError; + reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket); + emit connection->error(errorCode, reply->d_func()->errorString); + emit reply->finished(); + } + break; + default: + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) +{ + // this is only called for simple GET + + QHttpNetworkRequest &request = pair.first; + QHttpNetworkReply *reply = pair.second; + if (reply) { + reply->d_func()->clear(); + reply->d_func()->connection = connection; + reply->d_func()->autoDecompress = request.d->autoDecompress; + } + +#ifndef QT_NO_NETWORKPROXY + QByteArray header = QHttpNetworkRequestPrivate::header(request, + (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); +#else + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); +#endif + socket->write(header); + + alreadyPipelinedRequests.append(pair); +} + +void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest() +{ + requeueCurrentlyPipelinedRequests(); + close(); + resendCurrent = true; + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +} + //private slots void QHttpNetworkConnectionChannel::_q_readyRead() { - if (!socket) - return; // ### error if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) - connection->d_func()->receiveReply(socket, reply); + receiveReply(); } - // ### error } void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) { Q_UNUSED(bytes); - if (!socket) - return; // ### error // bytes have been written to the socket. write even more of them :) if (connection->d_func()->isSocketWriting(socket)) - connection->d_func()->sendRequest(socket); + sendRequest(); // otherwise we do nothing } void QHttpNetworkConnectionChannel::_q_disconnected() { - if (!socket) - return; // ### error // read the available data before closing if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) - connection->d_func()->receiveReply(socket, reply); + receiveReply(); } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { // re-sending request because the socket was in ClosingState QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } state = QHttpNetworkConnectionChannel::IdleState; + + requeueCurrentlyPipelinedRequests(); } void QHttpNetworkConnectionChannel::_q_connected() { - if (!socket) - return; // ### error - // improve performance since we get the request sent by the kernel ASAP socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown; + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! //channels[i].reconnectAttempts = 2; if (!pendingEncrypt) { state = QHttpNetworkConnectionChannel::IdleState; if (reply) - connection->d_func()->sendRequest(socket); + sendRequest(); else close(); } @@ -193,7 +777,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket // while "Reading" the _q_disconnected() will handle this. if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { if (reconnectAttempts-- > 0) { - connection->d_func()->resendCurrentRequest(socket); + closeAndResendCurrentRequest(); return; } else { send2Reply = true; @@ -206,7 +790,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket case QAbstractSocket::SocketTimeoutError: // try to reconnect/resend before sending an error. if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) { - connection->d_func()->resendCurrentRequest(socket); + closeAndResendCurrentRequest(); return; } send2Reply = true; @@ -244,13 +828,13 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket #ifndef QT_NO_NETWORKPROXY void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) { - emit connection->proxyAuthenticationRequired(proxy, auth, connection); + connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth); } #endif void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() { - connection->d_func()->sendRequest(socket); + sendRequest(); } #ifndef QT_NO_OPENSSL @@ -259,7 +843,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!socket) return; // ### error state = QHttpNetworkConnectionChannel::IdleState; - connection->d_func()->sendRequest(socket); + sendRequest(); } void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index f937669..220b72c 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -82,10 +82,14 @@ class QHttpNetworkReply; class QByteArray; class QHttpNetworkConnection; +#ifndef HttpMessagePair +typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair; +#endif + class QHttpNetworkConnectionChannel : public QObject { Q_OBJECT public: - enum ChannelState { + enum ChannelState { IdleState = 0, // ready to send request ConnectingState = 1, // connecting to host WritingState = 2, // writing the data @@ -112,12 +116,24 @@ public: bool ignoreAllSslErrors; QList<QSslError> ignoreSslErrorsList; #endif + + // HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining + enum PipeliningSupport { + PipeliningSupportUnknown, // default for a new connection + PipeliningProbablySupported, // after having received a server response that indicates support + PipeliningNotSupported // currently not used + }; + PipeliningSupport pipeliningSupported; + QList<HttpMessagePair> alreadyPipelinedRequests; + + QHttpNetworkConnectionChannel() : socket(0), state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), lastStatus(0), pendingEncrypt(false), reconnectAttempts(2), authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None) #ifndef QT_NO_OPENSSL , ignoreAllSslErrors(false) #endif + , pipeliningSupported(PipeliningSupportUnknown) , connection(0) {} @@ -127,6 +143,23 @@ public: void init(); void close(); + bool sendRequest(); + void receiveReply(); + + bool ensureConnection(); + + bool expand(bool dataComplete); + void allDone(); // reply header + body have been read + void handleStatus(); // called from allDone() + + void pipelineInto(HttpMessagePair &pair); + void requeueCurrentlyPipelinedRequests(); + void detectPipeliningSupport(); + + void closeAndResendCurrentRequest(); + + void eatWhitespace(); + protected slots: void _q_bytesWritten(qint64 bytes); // proceed sending void _q_readyRead(); // pending data to read diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c31c59c..eb8ecb5 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -418,20 +418,26 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket) } bool ok = parseStatus(fragment); state = ReadingHeaderState; - fragment.clear(); // next fragment - - if (!ok) + fragment.clear(); + if (!ok) { return -1; + } break; } else { c = 0; - bytes += socket->read(&c, 1); + int haveRead = socket->read(&c, 1); + if (haveRead == -1) + return -1; + bytes += haveRead; fragment.append(c); } // is this a valid reply? if (fragment.length() >= 5 && !fragment.startsWith("HTTP/")) + { + fragment.clear(); return -1; + } } @@ -568,7 +574,6 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData if (contentRead + haveRead == bodyLength) { state = AllDoneState; - socket->readAll(); // Read the rest to clean (CRLF) ### will break pipelining } contentRead += haveRead; @@ -588,8 +593,6 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff } else { bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable()); } - if (state == AllDoneState) - socket->readAll(); // Read the rest to clean (CRLF) ### will break pipelining contentRead += bytes; return bytes; } @@ -717,6 +720,33 @@ void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data) } +bool QHttpNetworkReplyPrivate::shouldEmitSignals() +{ + // for 401 & 407 don't emit the data signals. Content along with these + // responses are send only if the authentication fails. + return (statusCode != 401 && statusCode != 407); +} + +bool QHttpNetworkReplyPrivate::expectContent() +{ + // check whether we can expect content after the headers (rfc 2616, sec4.4) + if ((statusCode >= 100 && statusCode < 200) + || statusCode == 204 || statusCode == 304) + return false; + if (request.operation() == QHttpNetworkRequest::Head) + return !shouldEmitSignals(); + if (contentLength() == 0) + return false; + return true; +} + +void QHttpNetworkReplyPrivate::eraseData() +{ + compressedData.clear(); + responseData.clear(); +} + + // SSL support below #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 5aee067..f9503a9 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -177,6 +177,10 @@ public: void appendUncompressedReplyData(QByteDataBuffer &data); void appendCompressedReplyData(QByteDataBuffer &data); + bool shouldEmitSignals(); + bool expectContent(); + void eraseData(); + qint64 bytesAvailable() const; bool isChunked(); bool connectionCloseEnabled(); diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 693e1f4..8b2c4e9 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, QHttpNetworkRequest::Priority pri, const QUrl &newUrl) : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0), - autoDecompress(false) + autoDecompress(false), pipeliningAllowed(false) { } @@ -60,6 +60,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest priority = other.priority; uploadByteDevice = other.uploadByteDevice; autoDecompress = other.autoDecompress; + pipeliningAllowed = other.pipeliningAllowed; } QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() @@ -239,6 +240,16 @@ void QHttpNetworkRequest::setPriority(Priority priority) d->priority = priority; } +bool QHttpNetworkRequest::isPipeliningAllowed() const +{ + return d->pipeliningAllowed; +} + +void QHttpNetworkRequest::setPipeliningAllowed(bool b) +{ + d->pipeliningAllowed = b; +} + void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd) { d->uploadByteDevice = bd; diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index ffacb14..2468be9 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -106,6 +106,9 @@ public: Priority priority() const; void setPriority(Priority priority); + bool isPipeliningAllowed() const; + void setPipeliningAllowed(bool b); + void setUploadByteDevice(QNonContiguousByteDevice *bd); QNonContiguousByteDevice* uploadByteDevice() const; @@ -113,6 +116,7 @@ private: QSharedDataPointer<QHttpNetworkRequestPrivate> d; friend class QHttpNetworkRequestPrivate; friend class QHttpNetworkConnectionPrivate; + friend class QHttpNetworkConnectionChannel; }; class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate @@ -132,6 +136,7 @@ public: QHttpNetworkRequest::Priority priority; mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; + bool pipeliningAllowed; }; diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 479990a..fd47b34 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -516,6 +516,9 @@ void QNetworkAccessHttpBackend::postRequest() return; // no need to send the request! :) } + if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true) + httpRequest.setPipeliningAllowed(true); + httpReply = http->sendRequest(httpRequest); httpReply->setParent(this); #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index ce5f6c7..839bf31 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -804,7 +804,13 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac QAuthenticator *authenticator) { Q_Q(QNetworkAccessManager); - + // ### FIXME Tracking of successful authentications + // This code is a bit broken right now for SOCKS authentication + // first request: proxyAuthenticationRequired gets emitted, credentials gets saved + // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true, + // proxyAuthenticationRequired gets emitted again + // possible solution: some tracking inside the authenticator + // or a new function proxyAuthenticationSucceeded(true|false) if (proxy != backend->reply->lastProxyAuthentication) { QNetworkAuthenticationCredential *cred = fetchCachedCredentials(proxy); if (cred) { diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 721f8c4..7d838a3 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -167,6 +167,11 @@ QT_BEGIN_NAMESPACE When using this flag with sequential upload data, the ContentLengthHeader header must be set. + \value HttpPipeliningAllowed + Requests only, type: QVariant::Bool (default: false) + Indicates whether the QNetworkAccessManager code is + allowed to use HTTP pipelining with this request. + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index aaeed48..fb5ea52 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -76,6 +76,7 @@ public: CacheSaveControlAttribute, SourceIsFromCacheAttribute, DoNotBufferUploadDataAttribute, + HttpPipeliningAllowedAttribute, User = 1000, UserMax = 32767 diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index ee1369d..77b2a7e 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -162,16 +162,13 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, QWindowsSockInit bust; // makes sure WSAStartup was callled #endif - // Support for IDNA - QString lookup = QString::fromLatin1(QUrl::toAce(name)); - QHostInfoResult *result = new QHostInfoResult; result->autoDelete = false; QObject::connect(result, SIGNAL(resultsReady(QHostInfo)), receiver, member); int id = result->lookupId = theIdCounter.fetchAndAddRelaxed(1); - if (lookup.isEmpty()) { + if (name.isEmpty()) { QHostInfo info(id); info.setError(QHostInfo::HostNotFound); info.setErrorString(QObject::tr("No host name given")); @@ -182,7 +179,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, } QHostInfoAgent *agent = theAgent(); - agent->addHostName(lookup, result); + agent->addHostName(name, result); #if !defined QT_NO_THREAD if (!agent->isRunning()) @@ -226,13 +223,7 @@ QHostInfo QHostInfo::fromName(const QString &name) qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData()); #endif - if (!name.isEmpty()) - return QHostInfoAgent::fromName(QLatin1String(QUrl::toAce(name))); - - QHostInfo retval; - retval.d->err = HostNotFound; - retval.d->errorStr = QObject::tr("No host name given"); - return retval; + return QHostInfoAgent::fromName(name); } /*! diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 4a23399..e3d51e7 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -124,7 +124,6 @@ static void resolveLibrary() QHostInfo QHostInfoAgent::fromName(const QString &hostName) { QHostInfo results; - results.setHostName(hostName); #if defined(QHOSTINFO_DEBUG) qDebug("QHostInfoAgent::fromName(%s) looking up...", @@ -194,6 +193,22 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) #endif } + // IDN support + QByteArray aceHostname; + if (results.hostName().isEmpty()) { + // it's a hostname resolution + aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? QObject::tr("No host name given") : QObject::tr("Invalid hostname")); + return results; + } + } else { + // it's an IP reverse resolution + aceHostname = results.hostName().toLatin1(); + } + #if !defined (QT_NO_GETADDRINFO) // Call getaddrinfo, and place all IPv4 addresses at the start and // the IPv6 addresses at the end of the address list in results. @@ -205,12 +220,12 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) hints.ai_flags = Q_ADDRCONFIG; #endif - int result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); + int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); # ifdef Q_ADDRCONFIG if (result == EAI_BADFLAGS) { // if the lookup failed with AI_ADDRCONFIG set, try again without it hints.ai_flags = 0; - result = getaddrinfo(hostName.toLatin1().constData(), 0, &hints, &res); + result = getaddrinfo(aceHostname.constData(), 0, &hints, &res); } # endif @@ -264,7 +279,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) // use one QHostInfoAgent, but if more agents are introduced, locking // must be provided. QMutexLocker locker(::getHostByNameMutex()); - hostent *result = gethostbyname(hostName.toLatin1().constData()); + hostent *result = gethostbyname(aceHostname.constData()); if (result) { if (result->h_addrtype == AF_INET) { QList<QHostAddress> addresses; diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index 93dc720..c5dc2d2 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -52,6 +52,7 @@ #include <qlibrary.h> #include <qtimer.h> #include <qmutex.h> +#include <qurl.h> #include <private/qmutexpool_p.h> QT_BEGIN_NAMESPACE @@ -129,7 +130,6 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } QHostInfo results; - results.setHostName(hostName); #if defined(QHOSTINFO_DEBUG) qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)", @@ -178,12 +178,28 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } } + // IDN support + QByteArray aceHostname; + if (results.hostName().isEmpty()) { + // it's a hostname resolution + aceHostname = QUrl::toAce(hostName); + results.setHostName(hostName); + if (aceHostname.isEmpty()) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(hostName.isEmpty() ? QObject::tr("No host name given") : QObject::tr("Invalid hostname")); + return results; + } + } else { + // it's an IP reverse resolution + aceHostname = results.hostName().toLatin1(); + } + if (local_getaddrinfo && local_freeaddrinfo) { // Call getaddrinfo, and place all IPv4 addresses at the start // and the IPv6 addresses at the end of the address list in // results. qt_addrinfo *res; - int err = local_getaddrinfo(hostName.toLatin1().constData(), 0, 0, &res); + int err = local_getaddrinfo(aceHostname.constData(), 0, 0, &res); if (err == 0) { QList<QHostAddress> addresses; for (qt_addrinfo *p = res; p != 0; p = p->ai_next) { @@ -218,7 +234,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) } } else { // Fall back to gethostbyname, which only supports IPv4. - hostent *ent = gethostbyname(hostName.toLatin1().constData()); + hostent *ent = gethostbyname(aceHostname.constData()); if (ent) { char **p; QList<QHostAddress> addresses; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 34f7e7a..5b6a714 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -246,6 +246,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) #ifdef QT_OPENGL_ES_2 QDataBuffer<uchar> buffer(4*oldWidth*oldHeight); + buffer.resize(4*oldWidth*oldHeight); glReadPixels(0, 0, oldWidth, oldHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data()); // do an in-place conversion from GL_RGBA to GL_ALPHA diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index f19c94b..cc6fa88 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -45,44 +45,9 @@ #include <QtCore/qmetatype.h> #include <stdio.h> -QT_BEGIN_NAMESPACE +#include <private/qmetaobject_p.h> //for the flags. -// if the flags change, you MUST to change it in qmetaobject.cpp too -enum PropertyFlags { - Invalid = 0x00000000, - Readable = 0x00000001, - Writable = 0x00000002, - Resettable = 0x00000004, - EnumOrFlag = 0x00000008, - StdCppSet = 0x00000100, -// Override = 0x00000200, - Constant = 0x00000400, - Final = 0x00000800, - Designable = 0x00001000, - ResolveDesignable = 0x00002000, - Scriptable = 0x00004000, - ResolveScriptable = 0x00008000, - Stored = 0x00010000, - ResolveStored = 0x00020000, - Editable = 0x00040000, - ResolveEditable = 0x00080000, - User = 0x00100000, - ResolveUser = 0x00200000, - Notify = 0x00400000 -}; - -enum MethodFlags { - AccessPrivate = 0x00, - AccessProtected = 0x01, - AccessPublic = 0x02, - MethodMethod = 0x00, - MethodSignal = 0x04, - MethodSlot = 0x08, - MethodConstructor = 0x0c, - MethodCompatibility = 0x10, - MethodCloned = 0x20, - MethodScriptable = 0x40 -}; +QT_BEGIN_NAMESPACE uint qvariant_nameToType(const char* name) { @@ -205,10 +170,10 @@ void Generator::generateCode() QByteArray qualifiedClassNameIdentifier = cdef->qualified; qualifiedClassNameIdentifier.replace(':', '_'); - int index = 13; + int index = 14; fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); - fprintf(out, " %4d, // revision\n", 3); + fprintf(out, " %4d, // revision\n", 4); fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); index += cdef->classInfoList.count() * 2; @@ -229,6 +194,7 @@ void Generator::generateCode() isConstructible ? index : 0); fprintf(out, " %4d, // flags\n", 0); + fprintf(out, " %4d, // signalCount\n", cdef->signalList.count()); // @@ -1016,14 +982,7 @@ void Generator::generateSignal(FunctionDef *def,int index) else fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i); fprintf(out, " };\n"); - int n = 0; - for (i = 0; i < def->arguments.count(); ++i) - if (def->arguments.at(i).isDefault) - ++n; - if (n) - fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, %d, _a);\n", thisPtr.constData(), index, index + n); - else - fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); + fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); if (def->normalizedType.size()) fprintf(out, " return _t0;\n"); fprintf(out, "}\n"); diff --git a/tests/auto/qhostinfo/tst_qhostinfo.cpp b/tests/auto/qhostinfo/tst_qhostinfo.cpp index 4b60aca..abe49df 100644 --- a/tests/auto/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/qhostinfo/tst_qhostinfo.cpp @@ -303,7 +303,7 @@ void tst_QHostInfo::reverseLookup_data() // ### Use internal DNS instead. Discussed with Andreas. //QTest::newRow("classical.hexago.com") << QString("2001:5c0:0:2::24") << QStringList(QString("classical.hexago.com")) << 0; QTest::newRow("www.cisco.com") << QString("198.133.219.25") << QStringList(QString("origin-www.cisco.com")) << 0; - QTest::newRow("bogusexample.doenstexist.org") << QString("1::2::3::4") << QStringList() << 1; + QTest::newRow("bogus-name") << QString("1::2::3::4") << QStringList() << 1; } void tst_QHostInfo::reverseLookup() diff --git a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp index 4f24721..c4c33d5 100644 --- a/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp +++ b/tests/auto/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp @@ -99,6 +99,9 @@ private Q_SLOTS: void get401_data(); void get401(); + void getMultiple_data(); + void getMultiple(); + void getMultipleWithPipeliningAndMultiplePriorities(); }; @@ -763,5 +766,123 @@ void tst_QHttpNetworkConnection::nossl() } #endif + +void tst_QHttpNetworkConnection::getMultiple_data() +{ + QTest::addColumn<quint16>("connectionCount"); + QTest::addColumn<bool>("pipeliningAllowed"); + // send 100 requests. apache will usually force-close after 100 requests in a single tcp connection + QTest::addColumn<int>("requestCount"); + + QTest::newRow("6 connections, no pipelining, 100 requests") << quint16(6) << false << 100; + QTest::newRow("1 connection, no pipelining, 100 requests") << quint16(1) << false << 100; + QTest::newRow("6 connections, pipelining allowed, 100 requests") << quint16(2) << true << 100; + QTest::newRow("1 connection, pipelining allowed, 100 requests") << quint16(1) << true << 100; +} + +void tst_QHttpNetworkConnection::getMultiple() +{ + QFETCH(quint16, connectionCount); + QFETCH(bool, pipeliningAllowed); + QFETCH(int, requestCount); + + QHttpNetworkConnection connection(connectionCount, QtNetworkSettings::serverName()); + + QList<QHttpNetworkRequest*> requests; + QList<QHttpNetworkReply*> replies; + + for (int i = 0; i < requestCount; i++) { + // depending on what you use the results will vary. + // for the "real" results, use a URL that has "internet latency" for you. Then (6 connections, pipelining) will win. + // for LAN latency, you will possibly get that (1 connection, no pipelining) is the fastest + QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + // located in Berlin: + //QHttpNetworkRequest *request = new QHttpNetworkRequest(QUrl("http://klinsmann.nokia.trolltech.de/~berlin/qtcreatorad.gif")); + if (pipeliningAllowed) + request->setPipeliningAllowed(true); + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + // redundant + for (int i = 0; i < replies.length(); i++) + QVERIFY(replies.at(i)->isFinished()); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + +void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities() +{ + quint16 requestCount = 100; + + // use 2 connections. + QHttpNetworkConnection connection(2, QtNetworkSettings::serverName()); + + QList<QHttpNetworkRequest*> requests; + QList<QHttpNetworkReply*> replies; + + for (int i = 0; i < requestCount; i++) { + + QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + + if (i % 2 || i % 3) + request->setPipeliningAllowed(true); + + if (i % 3) + request->setPriority(QHttpNetworkRequest::HighPriority); + else if (i % 5) + request->setPriority(QHttpNetworkRequest::NormalPriority); + else if (i % 7) + request->setPriority(QHttpNetworkRequest::LowPriority); + + requests.append(request); + QHttpNetworkReply *reply = connection.sendRequest(*request); + replies.append(reply); + } + + QTime stopWatch; + stopWatch.start(); + int finishedCount = 0; + do { + QCoreApplication::instance()->processEvents(); + if (stopWatch.elapsed() >= 60000) + break; + + finishedCount = 0; + for (int i = 0; i < replies.length(); i++) + if (replies.at(i)->isFinished()) + finishedCount++; + + } while (finishedCount != replies.length()); + + // redundant + for (int i = 0; i < replies.length(); i++) + QVERIFY(replies.at(i)->isFinished()); + + qDebug() << "===" << stopWatch.elapsed() << "msec ==="; + + qDeleteAll(requests); + qDeleteAll(replies); +} + QTEST_MAIN(tst_QHttpNetworkConnection) #include "tst_qhttpnetworkconnection.moc" diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index d339803..7a9d016 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -2254,8 +2254,11 @@ void tst_QNetworkReply::ioGetFromHttpBrokenServer_data() QTest::addColumn<bool>("doDisconnect"); QTest::newRow("no-newline") << QByteArray("Hello World") << false; - QTest::newRow("just-newline") << QByteArray("\r\n") << false; - QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false; + + // these are OK now, we just eat the lonely newlines + //QTest::newRow("just-newline") << QByteArray("\r\n") << false; + //QTest::newRow("just-2newline") << QByteArray("\r\n\r\n") << false; + QTest::newRow("with-newlines") << QByteArray("Long first line\r\nLong second line") << false; QTest::newRow("with-newlines2") << QByteArray("\r\nSecond line") << false; QTest::newRow("with-newlines3") << QByteArray("ICY\r\nSecond line") << false; @@ -2931,7 +2934,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocket() QSignalSpy authenticationRequiredSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); QSignalSpy proxyAuthenticationRequiredSpy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); - QTestEventLoop::instance().enterLoop(1); + QTestEventLoop::instance().enterLoop(3); disconnect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); diff --git a/tests/auto/qquaternion/tst_qquaternion.cpp b/tests/auto/qquaternion/tst_qquaternion.cpp index ba546f1..1fb2d6b 100644 --- a/tests/auto/qquaternion/tst_qquaternion.cpp +++ b/tests/auto/qquaternion/tst_qquaternion.cpp @@ -466,6 +466,15 @@ void tst_QQuaternion::multiply_data() QTest::newRow("complex") << (qreal)1.0f << (qreal)2.0f << (qreal)3.0f << (qreal)7.0f << (qreal)4.0f << (qreal)5.0f << (qreal)6.0f << (qreal)8.0f; + + for (qreal w = -1.0f; w <= 1.0f; w += 0.5f) + for (qreal x = -1.0f; x <= 1.0f; x += 0.5f) + for (qreal y = -1.0f; y <= 1.0f; y += 0.5f) + for (qreal z = -1.0f; z <= 1.0f; z += 0.5f) { + QTest::newRow("exhaustive") + << x << y << z << w + << z << w << y << x; + } } void tst_QQuaternion::multiply() { diff --git a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp index c9b2325..dd34484 100644 --- a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp @@ -799,6 +799,7 @@ void tst_QSharedPointer::differentPointers() QVERIFY(baseptr.data() == aData); QVERIFY(aData == baseptr.data()); + QVERIFY(bool(operator==<Data,DiffPtrDerivedData>(baseptr, aData))); QVERIFY(baseptr == aData); QVERIFY(aData == baseptr); } diff --git a/tests/auto/qvariant/tst_qvariant.cpp b/tests/auto/qvariant/tst_qvariant.cpp index 9ad4482..709cba9 100644 --- a/tests/auto/qvariant/tst_qvariant.cpp +++ b/tests/auto/qvariant/tst_qvariant.cpp @@ -267,6 +267,8 @@ private slots: void convertByteArrayToBool_data() const; void toIntFromQString() const; void task256984_setValue(); + + void numericalConvert(); }; Q_DECLARE_METATYPE(QDate) @@ -3060,11 +3062,45 @@ void tst_QVariant::task256984_setValue() QVERIFY( !v2.isDetached() ); qVariantSetValue(v2, 3); //set an integer value - + QVERIFY( v1.isDetached() ); QVERIFY( v2.isDetached() ); } +void tst_QVariant::numericalConvert() +{ + QVariant vfloat(float(5.3)); + QVariant vdouble(double(5.3)); + QVariant vreal(qreal(5.3)); + QVariant vint(int(5)); + QVariant vuint(uint(5)); + QVariant vshort(short(5)); + QVariant vlonglong(quint64(5)); + QVariant vstringint(QString::fromLatin1("5")); + QVariant vstring(QString::fromLatin1("5.3")); + + QVector<QVariant *> vect; + vect << &vfloat << &vdouble << &vreal << &vint << &vuint << &vshort<< &vlonglong << &vstringint << &vstring; + + for(int i = 0; i < vect.size(); i++) { + double num = 5.3; + if (i >= 3 && i <= 7) + num = 5; + QVariant *v = vect.at(i); + QCOMPARE(v->toFloat() , float(num)); + QCOMPARE(float(v->toReal()) , float(num)); + QCOMPARE(float(v->toDouble()) , float(num)); + if(i != 8) { + QCOMPARE(v->toInt() , int(num)); + QCOMPARE(v->toUInt() , uint(num)); + QCOMPARE(v->toULongLong() , quint64(num)); + } + QCOMPARE(v->toString() , QString::number(num)); + } +} + + + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" diff --git a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp index 923838a..91dd28b 100644 --- a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp @@ -61,6 +61,7 @@ public slots: private slots: void setParentItem(); void setParentItem_deep(); + void setParentItem_deep_reversed(); void deleteItemWithManyChildren(); void setPos_data(); void setPos(); @@ -113,6 +114,19 @@ void tst_QGraphicsItem::setParentItem_deep() } } +void tst_QGraphicsItem::setParentItem_deep_reversed() +{ + QBENCHMARK { + QGraphicsRectItem *lastRect = new QGraphicsRectItem; + for (int i = 0; i < 100; ++i) { + QGraphicsRectItem *parentRect = new QGraphicsRectItem; + lastRect->setParentItem(parentRect); + lastRect = parentRect; + } + delete lastRect; + } +} + void tst_QGraphicsItem::deleteItemWithManyChildren() { QBENCHMARK { |