summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-07-12 01:00:58 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2019-07-12 12:00:31 +0200
commitb0a4c4808f54a2cbe64a75f1a5a341be420b584c (patch)
treed71f43142497c03df9fe7826b52512c4ed2153ef
parent47ad6c57d1873c3da362cfad7cc81dc0aa2720a4 (diff)
parentf3d40896c1d8601703fcbf30214e22f50eb72727 (diff)
Merge remote-tracking branch 'origin/5.12' into 5.13
Required a change to a #include; qquicksinglepointhandler.cpp was (at least on Android) only seeing QQuickSinglePointHandler as a forward declaration, so dereferencing it was a problem. The header that defines it does #include the one it replaces here. Change-Id: I6bc30ff9a91f55350172e4a4bcaaa7f99a2ffb28
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp201
-rw-r--r--src/qml/jsruntime/qv4promiseobject_p.h7
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp15
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp3
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp4
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp3
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp9
-rw-r--r--src/quick/items/qquickevents.cpp10
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp2
-rw-r--r--tests/auto/qml/qqmlpromise/data/promisechain.qml24
-rw-r--r--tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp15
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml50
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp43
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp82
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp8
16 files changed, 379 insertions, 99 deletions
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index 8450655334..40a0dfaa57 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -86,6 +86,7 @@ namespace QV4 {
namespace Promise {
const int PROMISE_REACTION_EVENT = QEvent::registerEventType();
+const int PROMISE_RESOLVE_THENABLE_EVENT = QEvent::registerEventType();
struct ReactionEvent : public QEvent
{
@@ -99,6 +100,18 @@ struct ReactionEvent : public QEvent
QV4::PersistentValue resolution;
};
+struct ResolveThenableEvent : public QEvent
+{
+ ResolveThenableEvent(ExecutionEngine *e, const PromiseObject *promise_, const Object *thenable_, const FunctionObject *then_)
+ : QEvent(QEvent::Type(PROMISE_RESOLVE_THENABLE_EVENT)), promise(e, *promise_), thenable(e, *thenable_), then(e, *then_)
+ {}
+
+ QV4::PersistentValue promise;
+ QV4::PersistentValue thenable;
+ QV4::PersistentValue then;
+};
+
+
} // namespace Promise
} // namespace QV4
QT_END_NAMESPACE
@@ -115,6 +128,11 @@ void ReactionHandler::addReaction(ExecutionEngine *e, const Value *reaction, con
QCoreApplication::postEvent(this, new ReactionEvent(e, reaction, value));
}
+void ReactionHandler::addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then)
+{
+ QCoreApplication::postEvent(this, new ResolveThenableEvent(e, promise, thenable, then));
+}
+
void ReactionHandler::customEvent(QEvent *event)
{
if (event)
@@ -122,6 +140,9 @@ void ReactionHandler::customEvent(QEvent *event)
const int type = event->type();
if (type == PROMISE_REACTION_EVENT)
executeReaction(static_cast<ReactionEvent*>(event));
+
+ if (type == PROMISE_RESOLVE_THENABLE_EVENT)
+ executeResolveThenable(static_cast<ResolveThenableEvent*>(event));
}
}
@@ -159,6 +180,7 @@ void ReactionHandler::executeReaction(ReactionEvent *event)
}
}
+
namespace {
class FunctionBuilder {
@@ -200,6 +222,26 @@ public:
}
+
+void ReactionHandler::executeResolveThenable(ResolveThenableEvent *event)
+{
+ Scope scope(event->then.engine());
+ JSCallData jsCallData(scope, 2);
+ PromiseObject *promise = event->promise.as<PromiseObject>();
+ ScopedFunctionObject resolve {scope, FunctionBuilder::makeResolveFunction(scope.engine, promise->d())};
+ ScopedFunctionObject reject {scope, FunctionBuilder::makeRejectFunction(scope.engine, promise->d())};
+ jsCallData->args[0] = resolve;
+ jsCallData.args[1] = reject;
+ jsCallData->thisObject = event->thenable.as<QV4::Object>();
+ event->then.as<const FunctionObject>()->call(jsCallData);
+ if (scope.engine->hasException) {
+ JSCallData rejectCallData(scope, 1);
+ rejectCallData->args[0] = scope.engine->catchException();
+ Scoped<RejectWrapper> reject {scope, scope.engine->memoryManager->allocate<QV4::RejectWrapper>()};
+ reject->call(rejectCallData);
+ }
+}
+
void Heap::PromiseObject::setState(PromiseObject::State state)
{
this->state = state;
@@ -363,23 +405,36 @@ void Heap::RejectWrapper::init()
Heap::FunctionObject::init();
}
+ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ // 25.4.3.1 Promise ( executor )
+ // 1. If NewTarget is undefined, throw a TypeError exception.
+ Scope scope(f);
+ THROW_TYPE_ERROR();
+}
ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
+ // 25.4.3.1 Promise ( executor )
+
Scope scope(f);
- if (argc != 1)
+ if (argc == 0) // If there are no arguments, argument 1 will be undefined ==> thus not callable ==> type error
THROW_TYPE_ERROR();
ScopedFunctionObject executor(scope, argv[0].as<const FunctionObject>());
- if (!executor)
- THROW_TYPE_ERROR();
+ if (!executor) //2. If IsCallable(executor) is false
+ THROW_TYPE_ERROR(); // throw a TypeError exception
Scoped<PromiseObject> a(scope, scope.engine->newPromiseObject());
if (scope.engine->hasException)
return Encode::undefined();
- a->d()->state = Heap::PromiseObject::Pending;
+ a->d()->state = Heap::PromiseObject::Pending; //4. Set promise.[[PromiseState]] to "pending"
+ // 5. Set promise.[[PromiseFulfillReactions]] to a new empty List.
+ // 6. Set promise.[[PromiseRejectReactions]] to a new empty List.
+ // 7. Set promise.[[PromiseIsHandled]] to false.
+ // happens in constructor of a
ScopedFunctionObject resolve(scope, FunctionBuilder::makeResolveFunction(scope.engine, a->d()));
ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(scope.engine, a->d()));
@@ -387,13 +442,15 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con
JSCallData jsCallData(scope, 2);
jsCallData->args[0] = resolve;
jsCallData->args[1] = reject;
- jsCallData->thisObject = a;
+ //jsCallData->thisObject = a; VERIFY corretness, but this should be undefined (see below)
- executor->call(jsCallData);
+ executor->call(jsCallData); // 9. Let completion be Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
if (scope.engine->hasException) {
- a->d()->state = Heap::PromiseObject::Rejected;
- a->d()->resolution.set(scope.engine, Value::fromReturnedValue(scope.engine->catchException()));
+ ScopedValue exception {scope, scope.engine->catchException()};
+ JSCallData callData {scope, 1};
+ callData.args[0] = exception;
+ reject->call(callData);
}
if (newTarget)
@@ -402,42 +459,42 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con
return a->asReturnedValue();
}
-ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
-{
- Scope scope(f);
- THROW_TYPE_ERROR();
-}
ReturnedValue PromiseCtor::method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.4.5Promise.resolve ( x )
Scope scope(f);
ExecutionEngine* e = scope.engine;
- if (!thisObject || !thisObject->isObject())
+ if (!thisObject || !thisObject->isObject()) // 2. If Type(C) is not Object, throw a TypeError exception
THROW_TYPE_ERROR();
- ScopedValue argument(scope);
+ ScopedValue x(scope);
if (argc < 1) {
- argument = Encode::undefined();
+ x = Encode::undefined();
} else {
- argument = argv[0];
+ x = argv[0];
}
- if (isPromise(argument) && argument->isObject()) {
+ // 3. If IsPromise(x) is true, then
+ if (isPromise(x) && x->isObject()) {
ScopedObject so(scope, thisObject);
- ScopedObject constructor(scope, argument->objectValue()->get(e->id_constructor()));
- if (so->d() == constructor->d())
- return argument->asReturnedValue();
+ // Let xConstructor be ? Get(x, "constructor").
+ ScopedObject constructor(scope, x->objectValue()->get(e->id_constructor()));
+ if (so->d() == constructor->d()) // If SameValue(xConstructor, C) is true, return x.
+ return x->asReturnedValue();
}
+ // Let promiseCapability be ? NewPromiseCapability(C).
Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
THROW_TYPE_ERROR();
+ // Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
ScopedValue undefined(scope, Value::undefinedValue());
ScopedFunctionObject resolve(scope, capability->d()->resolve);
- resolve->call(undefined, argument, 1);
+ resolve->call(undefined, x, 1);
return newPromise.asReturnedValue();
}
@@ -447,16 +504,18 @@ ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *t
Scope scope(f);
ExecutionEngine *e = scope.engine;
+ // 2. If Type(C) is not Object, throw a TypeError exception.
if (!thisObject || !thisObject->isObject())
THROW_TYPE_ERROR();
- ScopedValue argument(scope);
+ ScopedValue r(scope);
if (argc < 1) {
- argument = Encode::undefined();
+ r = Encode::undefined();
} else {
- argument = argv[0];
+ r = argv[0];
}
+ // 3. Let promiseCapability be ? NewPromiseCapability(C).
Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
@@ -465,7 +524,8 @@ ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *t
ScopedValue undefined(scope, Value::undefinedValue());
ScopedFunctionObject reject(scope, capability->d()->reject.as<const FunctionObject>());
- reject->call(undefined, argument, 1);
+ // Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).
+ reject->call(undefined, r, 1);
return newPromise.asReturnedValue();
}
@@ -475,6 +535,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
Scope scope(f);
ExecutionEngine* e = scope.engine;
+ // 2. If Type(C) is not Object, throw a TypeError exception.
if (!thisObject || !thisObject->isObject())
THROW_TYPE_ERROR();
@@ -777,6 +838,7 @@ void PromisePrototype::init(ExecutionEngine *engine, Object *ctor)
ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.5.3 Promise.prototype.then
Scope scope(f);
ExecutionEngine* e = scope.engine;
@@ -804,6 +866,7 @@ ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value
if (!constructor || scope.hasException())
THROW_TYPE_ERROR();
+ // 4. Let resultCapability be ? NewPromiseCapability(C).
ScopedObject nextPromise(scope, e->newPromiseObject(constructor, capability));
capability->d()->promise.set(scope.engine, nextPromise);
@@ -811,21 +874,25 @@ ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value
Scoped<PromiseReaction> rejectReaction(scope, Heap::PromiseReaction::createRejectReaction(scope.engine, capability, onRejected));
ScopedValue resolution(scope, promise->d()->resolution);
- if (promise->d()->isPending()) {
+ if (promise->d()->isPending()) { // 7. If promise.[[PromiseState]] is "pending"
{
+ // Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
ScopedArrayObject a(scope, promise->d()->fulfillReactions);
ScopedValue newValue(scope, fulfillReaction->d());
a->push_back(newValue);
}
{
+ // Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
ScopedArrayObject a(scope, promise->d()->rejectReactions);
ScopedValue newValue(scope, rejectReaction->d());
a->push_back(newValue);
}
- } else if (promise->d()->isFulfilled()) {
- fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
- } else if (promise->d()->isRejected()) {
+ } else if (promise->d()->isFulfilled()) { // 8. Else if promise.[[PromiseState]] is "fulfilled", then
+ // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
+ fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution); // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
+ } else if (promise->d()->isRejected()) { // 9. Else
+ // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « rejectReaction, reason »).
rejectReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
} else {
Q_ASSERT(false);
@@ -889,7 +956,6 @@ ReturnedValue CapabilitiesExecutorWrapper::virtualCall(const FunctionObject *f,
if (argc >= 2 && !argv[1].isUndefined())
capabilities->reject.set(scope.engine, argv[1]);
- // TODO: return?
return Encode::undefined();
}
@@ -929,27 +995,60 @@ ReturnedValue ResolveElementWrapper::virtualCall(const FunctionObject *f, const
ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.1.3.2 (ecmase-262/8.0)
+
Q_UNUSED(thisObject);
Scope scope(f);
const ResolveWrapper *self = static_cast<const ResolveWrapper*>(f);
Scoped<PromiseObject> promise(scope, self->d()->promise);
- if (self->d()->alreadyResolved || !promise->d()->isPending())
+ // 4. If alreadyRseolved.[[Value]] is true, return undefined
+ if (self->d()->alreadyResolved || !promise->d()->isPending()) // Why check for pending?
return Encode::undefined();
- ScopedValue value(scope);
+ // 5. Set alreadyResolved.[[Value]] to true
+ self->d()->alreadyResolved = true;
+
+ ScopedValue resolution(scope);
if (argc == 1) {
- value = argv[0];
+ resolution = argv[0];
} else {
- value = Encode::undefined();
+ resolution = Encode::undefined();
}
- self->d()->alreadyResolved = true;
- promise->d()->setState(Heap::PromiseObject::Fulfilled);
- promise->d()->resolution.set(scope.engine, value);
-
- promise->d()->triggerFullfillReactions(scope.engine);
+ if (!resolution->isObject()) { // 7 If Type(resolution) is not Object
+ // then Return FullFillPromise(promise, resolution)
+ // (FullFillPromise will return undefined, so we share the return with the other path which also returns undefined
+ promise->d()->setState(Heap::PromiseObject::Fulfilled);
+ promise->d()->resolution.set(scope.engine, resolution);
+ promise->d()->triggerFullfillReactions(scope.engine);
+ } else {
+ //PromiseObject *promise = resolution->as<PromiseObject>();
+ auto resolutionObject = resolution->as<Object>();
+ ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then")));
+
+ // 8. Let then be Get(resolution, then)
+ ScopedFunctionObject thenAction { scope, resolutionObject->get(thenName)};
+ // 9. If then is an abrupt completion, then
+ if (scope.engine->hasException) {
+ // Return RecjectPromise(promise, then.[[Value]]
+ ScopedValue thenValue {scope, scope.engine->catchException()};
+ promise->d()->setState(Heap::PromiseObject::Rejected);
+ promise->d()->resolution.set(scope.engine, thenValue);
+ promise->d()->triggerRejectReactions(scope.engine);
+ } else {
+ // 10. Let thenAction be then.[[Value]]
+ if (!thenAction) { // 11. If IsCallable(thenAction) is false
+ promise->d()->setState(Heap::PromiseObject::Fulfilled);
+ promise->d()->resolution.set(scope.engine, resolution);
+ promise->d()->triggerFullfillReactions(scope.engine);
+ } else {
+ // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »).
+ scope.engine->getPromiseReactionHandler()->addResolveThenable(scope.engine, promise.getPointer(), resolutionObject, thenAction);
+ }
+ }
+ }
return Encode::undefined();
}
@@ -972,11 +1071,25 @@ ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *t
value = Encode::undefined();
}
- self->d()->alreadyResolved = true;
- promise->d()->setState(Heap::PromiseObject::Rejected);
- promise->d()->resolution.set(scope.engine, value);
+ if (!isPromise(value)) {
+ self->d()->alreadyResolved = true;
+ promise->d()->setState(Heap::PromiseObject::Rejected);
+ promise->d()->resolution.set(scope.engine, value);
+
+ promise->d()->triggerRejectReactions(scope.engine);
- promise->d()->triggerRejectReactions(scope.engine);
+ } else {
+ PromiseObject *promise = value->as<PromiseObject>();
+ ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("catch")));
+
+ ScopedFunctionObject then(scope, promise->get(thenName));
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = *f;
+ jsCallData->args[1] = Encode::undefined();
+ jsCallData->thisObject = value;
+
+ then->call(jsCallData);
+ }
return Encode::undefined();
}
diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h
index bce59b19a7..8a3724e07d 100644
--- a/src/qml/jsruntime/qv4promiseobject_p.h
+++ b/src/qml/jsruntime/qv4promiseobject_p.h
@@ -62,6 +62,7 @@ struct PromiseCapability;
namespace Promise {
struct ReactionEvent;
+struct ResolveThenableEvent;
class ReactionHandler : public QObject
{
@@ -69,13 +70,15 @@ class ReactionHandler : public QObject
public:
ReactionHandler(QObject *parent = nullptr);
- virtual ~ReactionHandler();
+ virtual ~ReactionHandler() override;
void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value);
+ void addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then);
protected:
- void customEvent(QEvent *event);
+ void customEvent(QEvent *event) override;
void executeReaction(ReactionEvent *event);
+ void executeResolveThenable(ResolveThenableEvent *event);
};
} // Promise
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index de21537f27..f3d92cf200 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -51,13 +51,14 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
A QML representation of a QQuickEventPoint.
- It's possible to make bindings to properties of a \l SinglePointHandler's
- current point. For example:
+ It's possible to make bindings to properties of a handler's current
+ \l {SinglePointHandler::point}{point} or
+ \l {MultiPointHandler::centroid}{centroid}. For example:
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but when the point is released, or the current point is
+ an EventPoint; but after the point is released, or when the current point is
being handled by a different handler, \c position.x and \c position.y are 0.
\note This is practically identical to QtQuick::EventPoint; however an
@@ -68,7 +69,7 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
This allows you to make lifetime bindings to its properties.
- \sa SinglePointHandler::point
+ \sa SinglePointHandler::point, MultiPointHandler::centroid
*/
QQuickHandlerPoint::QQuickHandlerPoint()
@@ -106,12 +107,6 @@ void QQuickHandlerPoint::reset(const QQuickEventPoint *point)
m_scenePressPosition = point->scenePosition();
m_pressedButtons = event->buttons();
break;
- case QQuickEventPoint::Released:
- if (event->buttons() == Qt::NoButton) {
- reset();
- return;
- }
- break;
default:
break;
}
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index 61955cad03..d7566f0cd8 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickhoverhandler_p.h"
+#include <private/qquicksinglepointhandler_p_p.h>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,8 @@ Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
{
+ // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
+ d_func()->acceptedButtons = Qt::NoButton;
// Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 096fad2071..246686e4f4 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -256,6 +256,10 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
return false;
if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
+ // HoverHandler sets acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
+ if (event->device()->pointerType() != QQuickPointerDevice::Finger && acceptedButtons() != Qt::NoButton &&
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
+ return false;
return true;
}
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index c0fa39fad3..15fe0cc89e 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -74,9 +74,6 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
Q_D(QQuickSinglePointHandler);
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
- if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
- (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
- return false;
if (d->pointInfo.id()) {
// We already know which one we want, so check whether it's there.
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 40a4813527..255e47d73a 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -392,9 +392,6 @@ void QQuickTapHandler::updateTimeHeld()
from the release event about the point that was tapped:
\snippet pointerHandlers/tapHandlerOnTapped.qml 0
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
@@ -406,9 +403,6 @@ void QQuickTapHandler::updateTimeHeld()
it can be tapped again; but if the time until the next tap is less,
\l tapCount will increase. The \c eventPoint signal parameter contains
information from the release event about the point that was tapped.
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
@@ -422,9 +416,6 @@ void QQuickTapHandler::updateTimeHeld()
\l singleTapped, \l tapped, and \l tapCountChanged. The \c eventPoint
signal parameter contains information from the release event about the
point that was tapped.
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 28b217b7b3..8303c3fed1 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -913,10 +913,14 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b
passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
}
}
- if (oldGrabberHandler)
+ if (oldGrabberHandler) {
oldGrabberHandler->onGrabChanged(oldGrabberHandler, (grabber ? CancelGrabExclusive : UngrabExclusive), this);
- else if (oldGrabberItem && pointerEvent()->asPointerTouchEvent())
- oldGrabberItem->touchUngrabEvent();
+ } else if (oldGrabberItem) {
+ if (pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ else if (pointerEvent()->asPointerMouseEvent())
+ oldGrabberItem->mouseUngrabEvent();
+ }
// touchUngrabEvent() can result in the grabber being set to null (MPTA does that, for example).
// So set it again to ensure that final state is what we want.
m_exclusiveGrabber = QPointer<QObject>(grabber);
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
index cbe3be2e70..2f41e57324 100644
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
@@ -597,7 +597,7 @@ void SingleTest::run()
void SingleTest::runExternalTest()
{
- auto runTest = [=] (const char *header, TestCase::Result *result) {
+ auto runTest = [this] (const char *header, TestCase::Result *result) {
QTemporaryFile tempFile;
tempFile.open();
tempFile.write(header);
diff --git a/tests/auto/qml/qqmlpromise/data/promisechain.qml b/tests/auto/qml/qqmlpromise/data/promisechain.qml
new file mode 100644
index 0000000000..fa1809aef0
--- /dev/null
+++ b/tests/auto/qml/qqmlpromise/data/promisechain.qml
@@ -0,0 +1,24 @@
+import QtQml 2.0
+
+QtObject {
+ property int x: 0
+ id: root;
+ Component.onCompleted: {
+ new Promise((res) => {
+ res(1)
+ })
+ .then((data) => {
+ console.debug(data)
+ return new Promise((res) => {res(2)});
+ })
+ .then((data) => {
+ console.debug(data)
+ return new Promise((res) => {res(3)});
+ })
+ .then((data) => {
+ console.debug(data);
+ root.x = 42;
+ });
+ }
+
+}
diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
index 0f4bb5cdcc..41850d0263 100644
--- a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
+++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
@@ -82,6 +82,7 @@ private slots:
void then_fulfilled_non_callable();
void then_reject_non_callable();
void then_resolve_multiple_then();
+ void promiseChain();
private:
void execute_test(QString testName);
@@ -270,6 +271,20 @@ void tst_qqmlpromise::execute_test(QString testName)
QTRY_COMPARE(object->property("wasTestSuccessful").toBool(), true);
}
+void tst_qqmlpromise::promiseChain()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("promisechain.qml"));
+ QVERIFY(component.isReady());
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "3");
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QTRY_VERIFY(root->property("x") == 42);
+
+}
+
QTEST_MAIN(tst_qqmlpromise)
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
new file mode 100644
index 0000000000..dc7e5f6411
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ width: 640
+ height: 480
+ property alias touchpointPressed: tp1.pressed
+
+ Rectangle {
+ color: tp1.pressed ? "lightsteelblue" : drag.active ? "tomato" : "wheat"
+ width: 180
+ height: 180
+
+ DragHandler { id: drag }
+
+ MultiPointTouchArea {
+ anchors.fill: parent
+ touchPoints: [
+ TouchPoint { id: tp1 }
+ ]
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
index bf582b820b..cd18580ccf 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
@@ -56,6 +56,7 @@ private slots:
void touchDrag();
void touchesThenPinch();
void unloadHandlerWithPassiveGrab();
+ void dragHandlerInParentStealingGrabFromItem();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -301,6 +302,48 @@ void tst_MptaInterop::unloadHandlerWithPassiveGrab()
QTest::mouseRelease(window, Qt::LeftButton, 0, point); // QTBUG-73819: don't crash
}
+void tst_MptaInterop::dragHandlerInParentStealingGrabFromItem() // QTBUG-75025
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragParentOfMPTA.qml");
+ QQuickView * window = windowPtr.data();
+ auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
+
+ QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
+ QVERIFY(handler);
+ QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta);
+
+ // In QTBUG-75025 there is a QQ Controls Button; the MPTA here stands in for that,
+ // simply as an Item that grabs the mouse. The bug has nothing to do with MPTA specifically.
+
+ QPoint point(20, 20);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, point);
+ QCOMPARE(window->mouseGrabberItem(), mpta);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), true);
+
+ // Start dragging
+ // DragHandler keeps monitoring, due to its passive grab,
+ // and eventually steals the exclusive grab from MPTA
+ int dragStoleGrab = 0;
+ for (int i = 0; i < 4; ++i) {
+ point += QPoint(dragThreshold / 2, 0);
+ QTest::mouseMove(window, point);
+ if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler)
+ dragStoleGrab = i;
+ }
+ if (dragStoleGrab)
+ qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
+ QVERIFY(dragStoleGrab > 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, point);
+ QCOMPARE(handler->active(), false);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false);
+}
+
QTEST_MAIN(tst_MptaInterop)
#include "tst_multipointtoucharea_interop.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index cc8c567e5c..fb0192893f 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -54,6 +54,7 @@ private slots:
void defaultPropertyValues();
void touchDrag();
+ void mouseDrag_data();
void mouseDrag();
void dragFromMargin();
void touchDragMulti();
@@ -193,8 +194,22 @@ void tst_DragHandler::touchDrag()
QCOMPARE(centroidChangedSpy.count(), 5);
}
+void tst_DragHandler::mouseDrag_data()
+{
+ QTest::addColumn<Qt::MouseButtons>("acceptedButtons");
+ QTest::addColumn<Qt::MouseButtons>("dragButton");
+ QTest::newRow("left: drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::LeftButton);
+ QTest::newRow("right: don't drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::RightButton);
+ QTest::newRow("left: don't drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::LeftButton);
+ QTest::newRow("right or middle: drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::MiddleButton);
+}
+
void tst_DragHandler::mouseDrag()
{
+ QFETCH(Qt::MouseButtons, acceptedButtons);
+ QFETCH(Qt::MouseButtons, dragButton);
+ bool shouldDrag = bool(acceptedButtons & dragButton);
+
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "draggables.qml");
@@ -204,6 +219,7 @@ void tst_DragHandler::mouseDrag()
QVERIFY(ball);
QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>();
QVERIFY(dragHandler);
+ dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
@@ -211,45 +227,57 @@ void tst_DragHandler::mouseDrag()
QPointF ballCenter = ball->clipRect().center();
QPointF scenePressPos = ball->mapToScene(ballCenter);
QPoint p1 = scenePressPos.toPoint();
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTest::mousePress(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1);
QVERIFY(!dragHandler->active());
- QCOMPARE(dragHandler->centroid().position(), ballCenter);
- QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
- QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().velocity(), QVector2D());
- QCOMPARE(centroidChangedSpy.count(), 1);
+ if (shouldDrag) {
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().velocity(), QVector2D());
+ QCOMPARE(centroidChangedSpy.count(), 1);
+ }
p1 += QPoint(dragThreshold, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
- QCOMPARE(centroidChangedSpy.count(), 2);
- QVERIFY(!dragHandler->active());
+ if (shouldDrag) {
+ QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 2);
+ QVERIFY(!dragHandler->active());
+ }
p1 += QPoint(1, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->active());
+ if (shouldDrag)
+ QTRY_VERIFY(dragHandler->active());
+ else
+ QVERIFY(!dragHandler->active());
QCOMPARE(translationChangedSpy.count(), 0);
- QCOMPARE(centroidChangedSpy.count(), 3);
+ if (shouldDrag)
+ QCOMPARE(centroidChangedSpy.count(), 3);
QCOMPARE(dragHandler->translation().x(), 0.0);
QPointF sceneGrabPos = p1;
- QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ if (shouldDrag)
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
p1 += QPoint(19, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->active());
- QCOMPARE(dragHandler->centroid().position(), ballCenter);
- QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
- QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter));
- QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
- QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0);
- QCOMPARE(dragHandler->translation().y(), 0.0);
- QVERIFY(dragHandler->centroid().velocity().x() > 0);
- QCOMPARE(centroidChangedSpy.count(), 4);
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(shouldDrag ? dragHandler->active() : !dragHandler->active());
+ if (shouldDrag) {
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter));
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0);
+ QCOMPARE(dragHandler->translation().y(), 0.0);
+ QVERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 4);
+ }
+ QTest::mouseRelease(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1);
QTRY_VERIFY(!dragHandler->active());
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
- QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
- QCOMPARE(translationChangedSpy.count(), 1);
- QCOMPARE(centroidChangedSpy.count(), 5);
+ if (shouldDrag)
+ QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
+ QCOMPARE(translationChangedSpy.count(), shouldDrag ? 1 : 0);
+ QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0);
}
void tst_DragHandler::dragFromMargin() // QTBUG-74966
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
index 221e7df139..042b730799 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
@@ -34,6 +34,7 @@ Rectangle {
property alias pressed: tap.pressed
property bool checked: false
property alias gesturePolicy: tap.gesturePolicy
+ property point tappedPosition: Qt.point(0, 0)
signal tapped
signal canceled
@@ -51,6 +52,7 @@ Rectangle {
longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped
onTapped: {
tapFlash.start()
+ root.tappedPosition = point.scenePosition
root.tapped()
}
onCanceled: root.canceled()
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index 33cea69147..e77ea97518 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -107,6 +107,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold()
QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(buttonDragThreshold);
+ QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped()));
// DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release
@@ -122,6 +124,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold()
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool());
QCOMPARE(dragThresholdTappedSpy.count(), 1);
+ QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1);
+ QCOMPARE(tapHandler->point().position(), QPointF());
// DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold
dragThresholdTappedSpy.clear();
@@ -152,6 +156,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold()
QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(buttonDragThreshold);
+ QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped()));
// DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release
@@ -164,6 +170,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold()
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool());
QTRY_COMPARE(dragThresholdTappedSpy.count(), 1);
+ QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1);
+ QCOMPARE(tapHandler->point().position(), QPointF());
// DragThreshold button is no longer pressed if mouse goes beyond dragThreshold
dragThresholdTappedSpy.clear();