mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-08-01 20:07:22 +02:00
Merge pull request #21674 from calixteman/touch/fix-swallowed-pointerup
Let pointerup/cancel propagate in TouchManager
This commit is contained in:
commit
7fc7072f9c
@ -15,6 +15,10 @@
|
|||||||
|
|
||||||
import { OutputScale, stopEvent } from "./display_utils.js";
|
import { OutputScale, stopEvent } from "./display_utils.js";
|
||||||
|
|
||||||
|
function preventDefault(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
class TouchManager {
|
class TouchManager {
|
||||||
#container;
|
#container;
|
||||||
|
|
||||||
@ -135,8 +139,13 @@ class TouchManager {
|
|||||||
opt.capture = true;
|
opt.capture = true;
|
||||||
container.addEventListener("pointerdown", stopEvent, opt);
|
container.addEventListener("pointerdown", stopEvent, opt);
|
||||||
container.addEventListener("pointermove", stopEvent, opt);
|
container.addEventListener("pointermove", stopEvent, opt);
|
||||||
container.addEventListener("pointercancel", stopEvent, opt);
|
// `pointerup` and `pointercancel` are only default-prevented: a
|
||||||
container.addEventListener("pointerup", stopEvent, opt);
|
// `stopPropagation` in the capture phase also skips the bubble-phase
|
||||||
|
// listeners of the very node it's called on, hence swallowing them here
|
||||||
|
// would prevent any session in flight, e.g. an editor being resized, from
|
||||||
|
// ever being ended.
|
||||||
|
container.addEventListener("pointercancel", preventDefault, opt);
|
||||||
|
container.addEventListener("pointerup", preventDefault, opt);
|
||||||
this.#onPinchStart?.();
|
this.#onPinchStart?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import {
|
|||||||
selectEditor,
|
selectEditor,
|
||||||
selectEditors,
|
selectEditors,
|
||||||
switchToEditor,
|
switchToEditor,
|
||||||
|
unselectEditor,
|
||||||
waitForAnnotationModeChanged,
|
waitForAnnotationModeChanged,
|
||||||
waitForNoElement,
|
waitForNoElement,
|
||||||
waitForPointerUp,
|
waitForPointerUp,
|
||||||
@ -66,6 +67,26 @@ const drawLine = async (page, x0, y0, x1, y1) => {
|
|||||||
await awaitPromise(clickHandle);
|
await awaitPromise(clickHandle);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Draw an editor large enough to have room for two fingers on it, and leave it
|
||||||
|
// selected since that's what makes it resizable with a touchscreen.
|
||||||
|
const drawAndSelectEditor = async page => {
|
||||||
|
await switchToInk(page);
|
||||||
|
const { x, y, width, height } = await getRect(page, ".annotationEditorLayer");
|
||||||
|
await drawLine(
|
||||||
|
page,
|
||||||
|
x + 0.15 * width,
|
||||||
|
y + 0.1 * height,
|
||||||
|
x + 0.75 * width,
|
||||||
|
y + 0.35 * height
|
||||||
|
);
|
||||||
|
await commit(page);
|
||||||
|
|
||||||
|
return {
|
||||||
|
layer: { x, y, width, height },
|
||||||
|
editor: await getRect(page, getEditorSelector(0)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
describe("Ink Editor", () => {
|
describe("Ink Editor", () => {
|
||||||
describe("Basic operations", () => {
|
describe("Basic operations", () => {
|
||||||
let pages;
|
let pages;
|
||||||
@ -1445,13 +1466,12 @@ describe("Pinch to resize a drawing", () => {
|
|||||||
const before = await getRect(page, editorSelector);
|
const before = await getRect(page, editorSelector);
|
||||||
const startGap = Math.min(before.width, before.height) * 0.2;
|
const startGap = Math.min(before.width, before.height) * 0.2;
|
||||||
const endGap = Math.max(before.width, before.height) * 1.8;
|
const endGap = Math.max(before.width, before.height) * 1.8;
|
||||||
await pinch(
|
await pinch(page, {
|
||||||
page,
|
centerX: before.x + before.width / 2,
|
||||||
before.x + before.width / 2,
|
centerY: before.y + before.height / 2,
|
||||||
before.y + before.height / 2,
|
|
||||||
startGap,
|
startGap,
|
||||||
endGap
|
endGap,
|
||||||
);
|
});
|
||||||
const { width: after } = await getRect(page, editorSelector);
|
const { width: after } = await getRect(page, editorSelector);
|
||||||
|
|
||||||
expect(after)
|
expect(after)
|
||||||
@ -1461,3 +1481,96 @@ describe("Pinch to resize a drawing", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Resize with a touchscreen", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the resize session is ended when the finger is lifted while another one is down", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const { layer } = await drawAndSelectEditor(page);
|
||||||
|
|
||||||
|
// Grabbing a resizer starts a resize session, which disables the
|
||||||
|
// pointer events of the editor layer until the finger is lifted.
|
||||||
|
const resizer = await getRect(
|
||||||
|
page,
|
||||||
|
`${getEditorSelector(0)} .resizer.bottomRight`
|
||||||
|
);
|
||||||
|
await pinch(page, {
|
||||||
|
steps: 0,
|
||||||
|
startPoints: [
|
||||||
|
{
|
||||||
|
x: resizer.x + resizer.width / 2,
|
||||||
|
y: resizer.y + resizer.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: layer.x + 0.3 * layer.width,
|
||||||
|
y: layer.y + 0.9 * layer.height,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
afterFirstStart: () =>
|
||||||
|
page.waitForSelector(".annotationEditorLayer.disabled"),
|
||||||
|
afterFirstEnd: () =>
|
||||||
|
// The `pointerup` of the resizing finger must still be dispatched,
|
||||||
|
// else the resize session would never end and the editor would keep
|
||||||
|
// being resized by the plain mouse moves coming afterwards.
|
||||||
|
page.waitForSelector(".annotationEditorLayer:not(.disabled)"),
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Tap after a two-finger gesture", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that the tap following a two-finger gesture selects the editor", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const { layer, editor } = await drawAndSelectEditor(page);
|
||||||
|
const editorSelector = getEditorSelector(0);
|
||||||
|
|
||||||
|
// One finger on the editor, which starts a drag session, and a second
|
||||||
|
// one elsewhere on the page, which turns it into a two-finger gesture.
|
||||||
|
await pinch(page, {
|
||||||
|
steps: 0,
|
||||||
|
startPoints: [
|
||||||
|
{
|
||||||
|
x: editor.x + 0.5 * editor.width,
|
||||||
|
y: editor.y + 0.8 * editor.height,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: layer.x + 0.3 * layer.width,
|
||||||
|
y: layer.y + 0.9 * layer.height,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once every finger is up, no listener may be left behind to swallow
|
||||||
|
// the `pointerdown` of the next tap on the editor.
|
||||||
|
await unselectEditor(page, editorSelector);
|
||||||
|
await page.touchscreen.tap(
|
||||||
|
editor.x + 0.5 * editor.width,
|
||||||
|
editor.y + 0.8 * editor.height
|
||||||
|
);
|
||||||
|
await waitForSelectedEditor(page, editorSelector);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -606,23 +606,69 @@ async function dragAndDrop(page, selector, translations, steps = 1) {
|
|||||||
// between the two fingers changed by more than `MIN_TOUCH_DISTANCE_TO_PINCH`
|
// between the two fingers changed by more than `MIN_TOUCH_DISTANCE_TO_PINCH`
|
||||||
// (35 CSS pixels), hence the first moves are swallowed and the resulting zoom
|
// (35 CSS pixels), hence the first moves are swallowed and the resulting zoom
|
||||||
// factor is smaller than endGap / startGap.
|
// factor is smaller than endGap / startGap.
|
||||||
async function pinch(page, centerX, centerY, startGap, endGap, steps = 12) {
|
// Explicit start/end points can be used for tests which need an asymmetric
|
||||||
const finger0 = await page.touchscreen.touchStart(
|
// gesture, or hooks around each touch lifetime.
|
||||||
centerX - startGap,
|
async function pinch(
|
||||||
centerY
|
page,
|
||||||
);
|
{
|
||||||
const finger1 = await page.touchscreen.touchStart(
|
afterEnd = null,
|
||||||
centerX + startGap,
|
afterFirstEnd = null,
|
||||||
centerY
|
afterFirstStart = null,
|
||||||
);
|
afterStart = null,
|
||||||
const delta = (endGap - startGap) / steps;
|
beforeEnd = null,
|
||||||
for (let i = 1; i <= steps; i++) {
|
centerX = 0,
|
||||||
const gap = startGap + i * delta;
|
centerY = 0,
|
||||||
await finger0.move(centerX - gap, centerY);
|
centerDeltaX = 0,
|
||||||
await finger1.move(centerX + gap, centerY);
|
centerDeltaY = 0,
|
||||||
|
startGap = 0,
|
||||||
|
endGap = startGap,
|
||||||
|
endPoints = null,
|
||||||
|
startPoints = null,
|
||||||
|
steps = 12,
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
const normalizePoint = point =>
|
||||||
|
Array.isArray(point) ? { x: point[0], y: point[1] } : point;
|
||||||
|
const start = (
|
||||||
|
startPoints || [
|
||||||
|
{ x: centerX - startGap, y: centerY },
|
||||||
|
{ x: centerX + startGap, y: centerY },
|
||||||
|
]
|
||||||
|
).map(normalizePoint);
|
||||||
|
let end;
|
||||||
|
if (endPoints) {
|
||||||
|
end = endPoints.map(normalizePoint);
|
||||||
|
} else if (startPoints) {
|
||||||
|
end = start;
|
||||||
|
} else {
|
||||||
|
end = [
|
||||||
|
{ x: centerX + centerDeltaX - endGap, y: centerY + centerDeltaY },
|
||||||
|
{ x: centerX + centerDeltaX + endGap, y: centerY + centerDeltaY },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const finger0 = await page.touchscreen.touchStart(start[0].x, start[0].y);
|
||||||
|
await afterFirstStart?.(finger0);
|
||||||
|
const finger1 = await page.touchscreen.touchStart(start[1].x, start[1].y);
|
||||||
|
await afterStart?.([finger0, finger1]);
|
||||||
|
|
||||||
|
for (let i = 1; i <= steps; i++) {
|
||||||
|
const t = i / steps;
|
||||||
|
await finger0.move(
|
||||||
|
start[0].x + (end[0].x - start[0].x) * t,
|
||||||
|
start[0].y + (end[0].y - start[0].y) * t
|
||||||
|
);
|
||||||
|
await finger1.move(
|
||||||
|
start[1].x + (end[1].x - start[1].x) * t,
|
||||||
|
start[1].y + (end[1].y - start[1].y) * t
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await beforeEnd?.([finger0, finger1]);
|
||||||
await finger0.end();
|
await finger0.end();
|
||||||
|
await afterFirstEnd?.([finger0, finger1]);
|
||||||
await finger1.end();
|
await finger1.end();
|
||||||
|
await afterEnd?.([finger0, finger1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForPageChanging(page) {
|
function waitForPageChanging(page) {
|
||||||
|
|||||||
@ -1561,7 +1561,12 @@ describe("PDF viewer", () => {
|
|||||||
// Spread the two fingers from 50 to 200 pixels apart: the first
|
// Spread the two fingers from 50 to 200 pixels apart: the first
|
||||||
// moves are swallowed until the distance between them changed by
|
// moves are swallowed until the distance between them changed by
|
||||||
// more than 35 pixels, hence a zoom factor of about 200/85 = 2.4.
|
// more than 35 pixels, hence a zoom factor of about 200/85 = 2.4.
|
||||||
await pinch(page, originX, originY, 25, 100);
|
await pinch(page, {
|
||||||
|
centerX: originX,
|
||||||
|
centerY: originY,
|
||||||
|
startGap: 25,
|
||||||
|
endGap: 100,
|
||||||
|
});
|
||||||
await awaitPromise(rendered);
|
await awaitPromise(rendered);
|
||||||
|
|
||||||
const spanHandle = await page.evaluateHandle(() =>
|
const spanHandle = await page.evaluateHandle(() =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user