Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/blockly/core/gesture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,14 @@ export class Gesture {

this.mouseDownXY = new Coordinate(e.clientX, e.clientY);

// Re-establish the touch identifier for this gesture's starting pointer.
// If getGesture() cancelled a stale gesture for this pointer (a missed
// pointerup), that cancel() cleared the global touch identifier via
// dispose(). Without restoring it here, this gesture's terminating
// pointerup is rejected by Touch.shouldHandleEvent and the gesture is
// never disposed, permanently locking the workspace.
Touch.checkTouchIdentifier(e);

this.bindMouseEvents(e);

if (!this.isEnding_) {
Expand Down
22 changes: 22 additions & 0 deletions packages/blockly/tests/mocha/gesture_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,26 @@ suite('Gesture', function () {
this.workspace.id,
);
});

test('Duplicate pointerdown (missed pointerup) does not lock the workspace', function () {
// Some touch environments emit a pointerdown for a pointer that is already
// down (a missed pointerup). getGesture() handles this by cancelling the
// stale gesture and starting a new one; that new gesture must still be
// disposable so the workspace does not lock up permanently.
const target = this.workspace.getSvgGroup();
dispatchPointerEvent(target, 'pointerdown', {pointerId: 1});
assert.isTrue(
Blockly.Gesture.inProgress(),
'Precondition: first pointerdown must start a gesture.',
);
// Duplicate pointerdown for the same pointer id (missed pointerup).
dispatchPointerEvent(target, 'pointerdown', {pointerId: 1});
// Releasing the pointer must end the gesture.
dispatchPointerEvent(document, 'pointerup', {pointerId: 1});

assert.isFalse(
Blockly.Gesture.inProgress(),
'Gesture should not remain in progress after the pointer is released.',
);
});
});
Loading