From 919b723088b8617b202b92d80b8d0983e4fd9500 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Mon, 17 Aug 2009 10:17:27 +0200 Subject: Improve memory usage of the connectionlists inside QObject ... by not allocating space for slots in the vector. Before, the vector uses the signal index as index. The problem is that the slots and signal are mixed in the same index space. We solve the problem by having a different index space for the signal in the connectionlists vector. All we need to do is to add the information about the number of signals in the moc. Also, we are not connecting to cloned signal but only to the orginial ones. For example, destroyed(QObject * = 0) would generate two signal, we now only connect to the first one. This also improve a little bit the performence while activating signals since it removed one call to indexOfMethod. Reviewed-by: Brad --- src/corelib/animation/qvariantanimation.cpp | 15 +- src/corelib/animation/qvariantanimation_p.h | 2 - src/corelib/kernel/qmetaobject.cpp | 102 ++++----- src/corelib/kernel/qmetaobject_p.h | 74 ++++++ src/corelib/kernel/qobject.cpp | 335 +++++++++++++++++----------- src/corelib/kernel/qobject_p.h | 3 + src/corelib/kernel/qobjectdefs.h | 8 +- src/gui/graphicsview/qgraphicsitem.cpp | 2 +- src/gui/graphicsview/qgraphicsscene.cpp | 17 +- src/gui/graphicsview/qgraphicsscene_p.h | 2 +- src/tools/moc/generator.cpp | 53 +---- 11 files changed, 353 insertions(+), 260 deletions(-) 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/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(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 +#include 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(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 { 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(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(sender); QObject *r = const_cast(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/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 675fc0d..708ba09 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -6589,7 +6589,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/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)")); + if (!changedSignalIndex) { + changedSignalIndex = signalIndex("changed(QList)"); + } 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/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 #include -QT_BEGIN_NAMESPACE +#include //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(reinterpret_cast(&_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"); -- cgit v0.12 From 0af5e650d2e4a776c4c60f6ac3819605054663df Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Thu, 20 Aug 2009 15:52:41 +1000 Subject: Add some more tests for QQuaternion multiplication Reviewed-by: trustme --- tests/auto/qquaternion/tst_qquaternion.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) 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() { -- cgit v0.12 From e49f978b16f38bf51eb631e5bc04e46f4d8bdb9d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 20 Aug 2009 07:57:14 +0200 Subject: Instantiate the CP949 codec when building with -no-iconv Otherwise this codec would not be available. --- src/corelib/codecs/qtextcodec.cpp | 1 + 1 file changed, 1 insertion(+) 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 -- cgit v0.12 From 24f8cf971b330214f79757facc82d72981b7789e Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 20 Aug 2009 08:44:03 +0200 Subject: XdndLeave events have incorrect source id When sending XdndLeave events, we should use the effectiveWinId() of the drag source (just like we do when sending XdndEnter and XdndPosition events). Task-number: 255559 --- src/gui/kernel/qdnd_x11.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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); -- cgit v0.12 From 3ab10cf8690c1ab8ca4692338d7264f81fd5918b Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Wed, 5 Aug 2009 14:53:10 +0200 Subject: QNAM: Moved more functions into QHttpNetworkConnectionChannel Reviewed-by: Thiago --- src/network/access/qhttpnetworkconnection.cpp | 518 +-------------------- src/network/access/qhttpnetworkconnection_p.h | 15 +- .../access/qhttpnetworkconnectionchannel.cpp | 471 ++++++++++++++++++- .../access/qhttpnetworkconnectionchannel_p.h | 6 + src/network/access/qhttpnetworkreply.cpp | 27 ++ src/network/access/qhttpnetworkreply_p.h | 4 + src/network/access/qhttpnetworkrequest_p.h | 1 + 7 files changed, 519 insertions(+), 523 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index bf2d58a..36906a8 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -139,17 +139,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,232 +212,9 @@ 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(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(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, QNetworkReply::NetworkError errorCode) @@ -460,271 +226,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 +255,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 +295,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; @@ -906,7 +415,7 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + channels[i].sendRequest(); highPriorityQueue.removeAt(j); return; } @@ -919,24 +428,19 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke prepareRequest(messagePair); channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - sendRequest(socket); + channels[i].sendRequest(); lowPriorityQueue.removeAt(j); return; } } } -void QHttpNetworkConnectionPrivate::closeChannel(int channel) -{ - channels[channel].close(); -} - void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) { Q_Q(QHttpNetworkConnection); Q_ASSERT(socket); int i = indexOf(socket); - closeChannel(i); + channels[i].close(); channels[i].resendCurrent = true; QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); } @@ -992,7 +496,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 +535,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; @@ -1056,7 +560,7 @@ void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() if (channels[i].state == QHttpNetworkConnectionChannel::Wait4AuthState) { channels[i].state = QHttpNetworkConnectionChannel::IdleState; if (channels[i].reply) - sendRequest(socket); + channels[i].sendRequest(); } } } diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 99dda2a..ec8e1b4 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -168,10 +168,8 @@ public: QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request); void unqueueAndSendRequest(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 copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); // private slots @@ -179,9 +177,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 @@ -199,14 +197,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; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 5ffab76..ed602c4 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -51,6 +51,9 @@ # include # include # include + +#include "private/qnoncontiguousbytedevice_p.h" + #endif QT_BEGIN_NAMESPACE @@ -113,6 +116,462 @@ 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(channels[i].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 { + socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets! + state = QHttpNetworkConnectionChannel::WaitingState; + 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(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())); + } + // 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) { + connection->d_func()->resendCurrentRequest(socket); + } 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: { + 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 = connection->d_func()->errorDetail(QNetworkReply::ProtocolFailure, socket); + emit reply->finishedWithError(QNetworkReply::ProtocolFailure, reply->d_func()->errorString); + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + 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 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 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; + + // 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(socket); + sslSocket->connectToHostEncrypted(connectHost, connectPort); + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); + sslSocket->ignoreSslErrors(ignoreSslErrorsList); +#else + emitReplyError(socket, channels[index].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 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; +} + +void QHttpNetworkConnectionChannel::handleStatus() +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + int statusCode = reply->statusCode(); + bool resend = false; + + switch (statusCode) { + case 401: + case 407: + 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(); + + // also use async _q_startNextRequest so we dont break with closed + // proxy or server connections.. + resendCurrent = true; + 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); + } +} + + + //private slots void QHttpNetworkConnectionChannel::_q_readyRead() { @@ -121,7 +580,7 @@ void QHttpNetworkConnectionChannel::_q_readyRead() if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { state = QHttpNetworkConnectionChannel::ReadingState; if (reply) - connection->d_func()->receiveReply(socket, reply); + receiveReply(); } // ### error } @@ -133,7 +592,7 @@ void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) 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 } @@ -145,7 +604,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected() 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); @@ -167,7 +626,7 @@ void QHttpNetworkConnectionChannel::_q_connected() if (!pendingEncrypt) { state = QHttpNetworkConnectionChannel::IdleState; if (reply) - connection->d_func()->sendRequest(socket); + sendRequest(); else close(); } @@ -250,7 +709,7 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() { - connection->d_func()->sendRequest(socket); + sendRequest(); } #ifndef QT_NO_OPENSSL @@ -259,7 +718,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!socket) return; // ### error state = QHttpNetworkConnectionChannel::IdleState; - connection->d_func()->sendRequest(socket); + sendRequest(); } void QHttpNetworkConnectionChannel::_q_sslErrors(const QList &errors) diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index f937669..498fda9 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -126,6 +126,12 @@ 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() protected slots: void _q_bytesWritten(qint64 bytes); // proceed sending diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index c31c59c..cd843ab 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -717,6 +717,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_p.h b/src/network/access/qhttpnetworkrequest_p.h index ffacb14..d4c21e5 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -113,6 +113,7 @@ private: QSharedDataPointer d; friend class QHttpNetworkRequestPrivate; friend class QHttpNetworkConnectionPrivate; + friend class QHttpNetworkConnectionChannel; }; class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate -- cgit v0.12 From 09138f1f4eb45c196506ab94a455dc47cec3575a Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Fri, 7 Aug 2009 10:58:10 +0200 Subject: QNAM HTTP Code: Allow to specify the amount of TCP connections We will use this for auto testing. Reviewed-by: Thiago --- src/network/access/qhttpnetworkconnection.cpp | 22 +++++++++++++++++++++- src/network/access/qhttpnetworkconnection_p.h | 5 ++++- .../access/qhttpnetworkconnectionchannel.cpp | 4 +--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 36906a8..6888891 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -65,19 +65,32 @@ QT_BEGIN_NAMESPACE -const int QHttpNetworkConnectionPrivate::channelCount = 6; +const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6; 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) { @@ -573,6 +586,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() { } diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index ec8e1b4..3f928ec 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,9 @@ class QHttpNetworkConnectionPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QHttpNetworkConnection) public: + static const int defaultChannelCount; QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); + QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); @@ -189,7 +192,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 diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index ed602c4..2d33527 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 #include @@ -51,9 +52,6 @@ # include # include # include - -#include "private/qnoncontiguousbytedevice_p.h" - #endif QT_BEGIN_NAMESPACE -- cgit v0.12 From 67376be28ca51930ff0f4fad2dd58f53968655a9 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Mon, 17 Aug 2009 16:15:18 +0200 Subject: QNAM HTTP Pipelining HTTP Pipelining should improve the performance of HTTP requests for high latency network links. Since some servers/proxies could have problems with it, it is disabled by default. Set the HttpPipeliningAllowed attribute of a QNetworkRequest to enable it for that request. Reviewed-by: Thiago --- demos/browser/networkaccessmanager.h | 9 ++ src/network/access/qhttpnetworkconnection.cpp | 165 +++++++++++++++++-- src/network/access/qhttpnetworkconnection_p.h | 10 +- .../access/qhttpnetworkconnectionchannel.cpp | 177 ++++++++++++++++++--- .../access/qhttpnetworkconnectionchannel_p.h | 29 +++- src/network/access/qhttpnetworkreply.cpp | 17 +- src/network/access/qhttpnetworkrequest.cpp | 13 +- src/network/access/qhttpnetworkrequest_p.h | 4 + src/network/access/qnetworkaccesshttpbackend.cpp | 3 + src/network/access/qnetworkaccessmanager.cpp | 8 +- src/network/access/qnetworkrequest.cpp | 5 + src/network/access/qnetworkrequest.h | 1 + .../tst_qhttpnetworkconnection.cpp | 121 ++++++++++++++ tests/auto/qnetworkreply/tst_qnetworkreply.cpp | 9 +- 14 files changed, 519 insertions(+), 52 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 +#include 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 sslTrustedHostList; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 6888891..397e0b5 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -67,6 +67,11 @@ QT_BEGIN_NAMESPACE 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), @@ -414,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); @@ -428,8 +450,9 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - channels[i].sendRequest(); + // remove before sendRequest! else we might pipeline the same request again highPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } @@ -441,23 +464,113 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke prepareRequest(messagePair); channels[i].request = messagePair.first; channels[i].reply = messagePair.second; - channels[i].sendRequest(); + // remove before sendRequest! else we might pipeline the same request again lowPriorityQueue.removeAt(j); + channels[i].sendRequest(); return; } } } -void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) +// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel +void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket) { - Q_Q(QHttpNetworkConnection); - Q_ASSERT(socket); + // return fast if there is nothing to pipeline + if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) + return; + int i = indexOf(socket); - channels[i].close(); - channels[i].resendCurrent = true; - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + + 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]); + } +} + +// returns true when the processing of a queue has been done +bool QHttpNetworkConnectionPrivate::fillPipeline(QList &queue, QHttpNetworkConnectionChannel &channel) +{ + 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); @@ -560,16 +673,29 @@ 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) @@ -739,6 +865,19 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList &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 3f928ec..af764ed 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -155,6 +155,8 @@ 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(); @@ -169,9 +171,12 @@ 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); - void resendCurrentRequest(QAbstractSocket *socket); + + void fillPipeline(QAbstractSocket *socket); + bool fillPipeline(QList &queue, QHttpNetworkConnectionChannel &channel); void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); @@ -212,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 2d33527..221c27c 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -165,8 +165,8 @@ bool QHttpNetworkConnectionChannel::sendRequest() bytesTotal = request.contentLength(); } else { - socket->flush(); // ### Remove this when pipelining is implemented. We want less TCP packets! state = QHttpNetworkConnectionChannel::WaitingState; + sendRequest(); break; } // write the initial chunk together with the headers @@ -242,6 +242,11 @@ bool QHttpNetworkConnectionChannel::sendRequest() 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 @@ -276,7 +281,7 @@ void QHttpNetworkConnectionChannel::receiveReply() } else { // try to reconnect/resend before sending an error. if (reconnectAttempts-- > 0) { - connection->d_func()->resendCurrentRequest(socket); + closeAndResendCurrentRequest(); } else if (reply) { reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket); emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); @@ -292,14 +297,20 @@ void QHttpNetworkConnectionChannel::receiveReply() switch (state) { case QHttpNetworkReplyPrivate::NothingDoneState: case QHttpNetworkReplyPrivate::ReadingStatusState: { + eatWhitespace(); qint64 statusBytes = reply->d_func()->readStatus(socket); - if (statusBytes == -1) { - // error reading the status, close the socket and emit error - socket->close(); + 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; @@ -410,6 +421,9 @@ bool QHttpNetworkConnectionChannel::ensureConnection() 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 @@ -520,6 +534,92 @@ void QHttpNetworkConnectionChannel::allDone() // 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::Connected) { + // 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::Connected) + ) { + 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() @@ -531,8 +631,8 @@ void QHttpNetworkConnectionChannel::handleStatus() bool resend = false; switch (statusCode) { - case 401: - case 407: + 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(); @@ -547,10 +647,15 @@ void QHttpNetworkConnectionChannel::handleStatus() reply->d_func()->eraseData(); - // also use async _q_startNextRequest so we dont break with closed - // proxy or server connections.. - resendCurrent = true; - QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + 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(); @@ -568,26 +673,51 @@ void QHttpNetworkConnectionChannel::handleStatus() } } +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(channels[i].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) 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)) sendRequest(); @@ -596,8 +726,6 @@ void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) 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; @@ -608,17 +736,18 @@ void QHttpNetworkConnectionChannel::_q_disconnected() 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) { @@ -650,7 +779,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; @@ -663,7 +792,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; @@ -701,7 +830,7 @@ 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 diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 498fda9..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 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 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 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) {} @@ -126,13 +142,24 @@ 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 cd843ab..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; } 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 d4c21e5..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; @@ -133,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/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("connectionCount"); + QTest::addColumn("pipeliningAllowed"); + // send 100 requests. apache will usually force-close after 100 requests in a single tcp connection + QTest::addColumn("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 requests; + QList 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 requests; + QList 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("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*))); -- cgit v0.12 From dd5877fe48d647f1da20995c4d833d76ed43fd84 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 20 Aug 2009 11:40:30 +0200 Subject: Windows CE compile fix Reviewed-by: ariya --- src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v0.12 From 264eb347595cc92a444e3908831d89acef1f05b3 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 20 Aug 2009 11:58:20 +0200 Subject: Fix QVariant::toFloat() and QVariant::toReal Reviewed-by: Thierry --- src/corelib/kernel/qvariant.cpp | 4 ++-- src/corelib/kernel/qvariant.h | 1 + tests/auto/qvariant/tst_qvariant.cpp | 38 +++++++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) 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(d, handler, ok, d.data.d); + return qNumVariantToHelper(d, handler, ok, d.data.f); } /*! @@ -2484,7 +2484,7 @@ float QVariant::toFloat(bool *ok) const */ qreal QVariant::toReal(bool *ok) const { - return qNumVariantToHelper(d, handler, ok, d.data.d); + return qNumVariantToHelper(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/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 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" -- cgit v0.12 From 909cdf082fd99ce8bdcb99a9db38d09f52d869aa Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Thu, 20 Aug 2009 10:07:00 +0200 Subject: Performance issue in QGraphicsItem::addParentItem while building the scene bottom-up. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When adding n items, the depth was computed n² times. Adding lazy computation fixes this performance issue. Reviewed-by: Andreas --- src/gui/graphicsview/qgraphicsitem.cpp | 42 ++++++++++++++++++---- src/gui/graphicsview/qgraphicsitem_p.h | 8 +++-- .../graphicsview/qgraphicsscenebsptreeindex.cpp | 4 +-- .../benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp | 14 ++++++++ 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 708ba09..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(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(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; 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 focusProxyRefs; QGraphicsItem *subFocusItem; 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/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 { -- cgit v0.12 From c7c7b364d9816d03c7475d6592d50dec5401c151 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 18 Aug 2009 18:52:45 +0200 Subject: Fix literal IPv6 hostname resolution in QHostInfo. With the series of commits ending in ff1280178, I made QUrl::toAce fail if the parameter is not a well-formed hostname (i.e., if it violates STD3). IPv6 hostnames do, so we can't preemptively run ToACE. Instead, delay running ToACE until we've tried literal matching. Reviewed-by: TrustMe --- src/network/kernel/qhostinfo.cpp | 15 +++------------ src/network/kernel/qhostinfo_unix.cpp | 23 +++++++++++++++++++---- src/network/kernel/qhostinfo_win.cpp | 21 ++++++++++++++++++--- tests/auto/qhostinfo/tst_qhostinfo.cpp | 2 +- 4 files changed, 41 insertions(+), 20 deletions(-) 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 addresses; diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index 93dc720..8fd3a3f 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -129,7 +129,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 +177,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 addresses; for (qt_addrinfo *p = res; p != 0; p = p->ai_next) { @@ -218,7 +233,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 addresses; 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() -- cgit v0.12 From 5fa19cbf945f43146f2b0e0f93da53fec27fd543 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 20 Aug 2009 10:22:15 +0200 Subject: Remove a few warnings when compiling Qt and unexport some functions. Make some functions static that are not used anywhere but in the current file. Others that are used, add the declaration to the _p.h to ensure we don't forget about them. Finally, there's no need to enable debugging code if it's not used anywhere. Reviewed-by: TrustMe --- src/corelib/global/qlibraryinfo.cpp | 2 +- src/corelib/io/qdir.cpp | 2 +- src/corelib/kernel/qabstracteventdispatcher_p.h | 2 ++ src/corelib/kernel/qvariant_p.h | 3 +++ src/corelib/statemachine/qstatemachine_p.h | 2 ++ src/corelib/tools/qregexp.cpp | 2 +- src/dbus/qdbusmessage.cpp | 2 +- src/gui/image/qbmphandler.cpp | 8 ++++---- src/gui/image/qiconloader_p.h | 1 + src/gui/kernel/qshortcutmap.cpp | 4 ++-- src/gui/styles/gtksymbols.cpp | 2 +- 11 files changed, 19 insertions(+), 11 deletions(-) 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/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 +#include 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/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/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/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); -- cgit v0.12 From 9127fd802cb9127939dc41d125a5ac59d4876a71 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 20 Aug 2009 12:43:27 +0200 Subject: Autotest: add one more check for operator== I'm running out of ideas as to why MSVC.NET 2003 cannot do this test: baseptr == aData If I do baseptr.data() == aData, it works. And the operator== that it should be calling does exactly that. So my only clue so far is that it's calling some other operator== -- which doesn't make sense, since there is no other. --- tests/auto/qsharedpointer/tst_qsharedpointer.cpp | 1 + 1 file changed, 1 insertion(+) 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==(baseptr, aData))); QVERIFY(baseptr == aData); QVERIFY(aData == baseptr); } -- cgit v0.12 From 4a212d6c2ae82a1c5b7c9395e5a84dad8dd0b24c Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Thu, 20 Aug 2009 13:09:52 +0200 Subject: QNAM HTTP Code: Compile fix Build was broken when Qt3 support stuff was not enabled Reviewed-by: TrustMe --- src/network/access/qhttpnetworkconnectionchannel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 221c27c..334c31d 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -539,7 +539,7 @@ void QHttpNetworkConnectionChannel::allDone() // move next from pipeline to current request if (!alreadyPipelinedRequests.isEmpty()) { - if (resendCurrent || reply->d_func()->connectionCloseEnabled() || socket->state() != QAbstractSocket::Connected) { + if (resendCurrent || reply->d_func()->connectionCloseEnabled() || socket->state() != QAbstractSocket::ConnectedState) { // move the pipelined ones back to the main queue requeueCurrentlyPipelinedRequests(); } else { @@ -586,7 +586,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport() // check for not having connection close && (!reply->d_func()->connectionCloseEnabled()) // check if it is still connected - && (socket->state() == QAbstractSocket::Connected) + && (socket->state() == QAbstractSocket::ConnectedState) ) { pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported; } else { -- cgit v0.12 From 80ce368eec04bea154846dbf0f3f333089a5a015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trond=20Kjern=C3=A5sen?= Date: Thu, 20 Aug 2009 13:19:09 +0200 Subject: Fixed an assert in debug mode when drawing text with the GL 2 engine on embedded. Worked in release because we don't really use any internals in QDataBuffer that sets the internal size correctly. Reviewed-by: Samuel --- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 1 + 1 file changed, 1 insertion(+) 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 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 -- cgit v0.12 From 4460cced8935705a79ab6ebce9e57c46ed8969e1 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Thu, 20 Aug 2009 13:26:11 +0200 Subject: compile fix for Windows Reviewed-by: TrustMe --- src/network/kernel/qhostinfo_win.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp index 8fd3a3f..c5dc2d2 100644 --- a/src/network/kernel/qhostinfo_win.cpp +++ b/src/network/kernel/qhostinfo_win.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE -- cgit v0.12 From 87d7479fcc5f089ac164c5336b405d0299244c43 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Thu, 20 Aug 2009 13:26:38 +0200 Subject: QNAM HTTP Code: Compile fix 2 Those @$#! ifndefs. Reviewed-by: TrustMe --- src/network/access/qhttpnetworkconnectionchannel.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 334c31d..37386ea 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -154,8 +154,7 @@ bool QHttpNetworkConnectionChannel::sendRequest() QByteArray header = QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); #else - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - false); + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); #endif socket->write(header); QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice(); @@ -455,7 +454,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection() sslSocket->ignoreSslErrors(); sslSocket->ignoreSslErrors(ignoreSslErrorsList); #else - emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError); + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError); #endif } else { socket->connectToHost(connectHost, connectPort); @@ -689,8 +688,7 @@ void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) QByteArray header = QHttpNetworkRequestPrivate::header(request, (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)); #else - QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, - false); + QByteArray header = QHttpNetworkRequestPrivate::header(request, false); #endif socket->write(header); -- cgit v0.12 From 206c238d5ea9601d7dae77a3a45943a1cfea2747 Mon Sep 17 00:00:00 2001 From: Prasanth Ullattil Date: Thu, 20 Aug 2009 13:16:42 +0200 Subject: Flickering "Whatsthis" pointer in Cocoa Since mouse tracking is always enabled on all QCocoaView-s, we are getting mouseMoved events for both parent and child (if mouse is over the child). In such cases the mouseMoved events are ignored for the parent view. We are using the native NSCursor stack for setting the override cursor. The current implementation for changeOverrideCursor is modified to keep this stack in sync with Qt's internal list. Task-number: 258173 Reviewed-by: Morten Sorvig --- src/gui/kernel/qapplication.cpp | 13 +++++++++++++ src/gui/kernel/qcocoaview_mac.mm | 10 +++++++++- src/gui/kernel/qt_cocoa_helpers_mac.mm | 8 ++++++++ src/gui/kernel/qt_cocoa_helpers_mac_p.h | 1 + 4 files changed, 31 insertions(+), 1 deletion(-) 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 +#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/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(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); -- cgit v0.12