Merge pull request #15230 from calixteman/bug1781762

[Editor] Avoid editor creation/selection on right click (bug 1781762)
This commit is contained in:
calixteman 2022-07-27 18:27:55 +02:00 committed by GitHub
commit c9a4062c37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 18 deletions

View File

@ -21,8 +21,8 @@
/** @typedef {import("../../web/interfaces").IL10n} IL10n */ /** @typedef {import("../../web/interfaces").IL10n} IL10n */
import { AnnotationEditorType, shadow } from "../../shared/util.js"; import { AnnotationEditorType, shadow } from "../../shared/util.js";
import { bindEvents, KeyboardManager } from "./tools.js";
import { binarySearchFirstItem } from "../display_utils.js"; import { binarySearchFirstItem } from "../display_utils.js";
import { bindEvents } from "./tools.js";
import { FreeTextEditor } from "./freetext.js"; import { FreeTextEditor } from "./freetext.js";
import { InkEditor } from "./ink.js"; import { InkEditor } from "./ink.js";
@ -577,6 +577,12 @@ class AnnotationEditorLayer {
* @param {PointerEvent} event * @param {PointerEvent} event
*/ */
pointerup(event) { pointerup(event) {
const isMac = KeyboardManager.platform.isMac;
if (event.button !== 0 || (event.ctrlKey && isMac)) {
// Don't create an editor on right click.
return;
}
if (event.target !== this.div) { if (event.target !== this.div) {
return; return;
} }
@ -594,6 +600,12 @@ class AnnotationEditorLayer {
* @param {PointerEvent} event * @param {PointerEvent} event
*/ */
pointerdown(event) { pointerdown(event) {
const isMac = KeyboardManager.platform.isMac;
if (event.button !== 0 || (event.ctrlKey && isMac)) {
// Do nothing on right click.
return;
}
if (event.target !== this.div) { if (event.target !== this.div) {
return; return;
} }

View File

@ -253,6 +253,7 @@ class AnnotationEditor {
if (event.button !== 0 || (event.ctrlKey && isMac)) { if (event.button !== 0 || (event.ctrlKey && isMac)) {
// Avoid to focus this editor because of a non-left click. // Avoid to focus this editor because of a non-left click.
event.preventDefault(); event.preventDefault();
return;
} }
if ( if (

View File

@ -217,6 +217,10 @@ class FreeTextEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
enableEditMode() { enableEditMode() {
if (this.isInEditMode()) {
return;
}
this.parent.setEditingState(false); this.parent.setEditingState(false);
this.parent.updateToolbar(AnnotationEditorType.FREETEXT); this.parent.updateToolbar(AnnotationEditorType.FREETEXT);
super.enableEditMode(); super.enableEditMode();
@ -230,6 +234,10 @@ class FreeTextEditor extends AnnotationEditor {
/** @inheritdoc */ /** @inheritdoc */
disableEditMode() { disableEditMode() {
if (!this.isInEditMode()) {
return;
}
this.parent.setEditingState(true); this.parent.setEditingState(true);
super.disableEditMode(); super.disableEditMode();
this.overlayDiv.classList.add("enabled"); this.overlayDiv.classList.add("enabled");
@ -239,6 +247,10 @@ class FreeTextEditor extends AnnotationEditor {
this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus); this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus);
this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur); this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur);
// On Chrome, the focus is given to <body> when contentEditable is set to
// false, hence we focus the div.
this.div.focus();
// In case the blur callback hasn't been called. // In case the blur callback hasn't been called.
this.isEditing = false; this.isEditing = false;
} }

View File

@ -244,6 +244,7 @@ class KeyboardManager {
return; return;
} }
callback.bind(self)(); callback.bind(self)();
event.stopPropagation();
event.preventDefault(); event.preventDefault();
} }
} }

View File

@ -25,7 +25,7 @@ describe("Editor", () => {
let pages; let pages;
beforeAll(async () => { beforeAll(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer");
}); });
afterAll(async () => { afterAll(async () => {
@ -204,13 +204,13 @@ describe("Editor", () => {
it("must check that aria-owns is correct", async () => { it("must check that aria-owns is correct", async () => {
await Promise.all( await Promise.all(
pages.map(async ([browserName, page]) => { pages.map(async ([browserName, page]) => {
const [adobeComRect, oldAriaOwns] = await page.$eval( const [stacksRect, oldAriaOwns] = await page.$eval(
".textLayer", ".textLayer",
el => { el => {
for (const span of el.querySelectorAll( for (const span of el.querySelectorAll(
`span[role="presentation"]` `span[role="presentation"]`
)) { )) {
if (span.innerText.includes("adobe.com")) { if (span.innerText.includes("Stacks are simple to create")) {
span.setAttribute("pdfjs", true); span.setAttribute("pdfjs", true);
const { x, y, width, height } = span.getBoundingClientRect(); const { x, y, width, height } = span.getBoundingClientRect();
return [ return [
@ -227,21 +227,13 @@ describe("Editor", () => {
const data = "Hello PDF.js World !!"; const data = "Hello PDF.js World !!";
await page.mouse.click( await page.mouse.click(
adobeComRect.x + adobeComRect.width + 10, stacksRect.x + stacksRect.width + 1,
adobeComRect.y + adobeComRect.height / 2 stacksRect.y + stacksRect.height / 2
); );
await page.type(`${editorPrefix}5 .internal`, data); await page.type(`${editorPrefix}5 .internal`, data);
const editorRect = await page.$eval(`${editorPrefix}5`, el => {
const { x, y, width, height } = el.getBoundingClientRect();
return { x, y, width, height };
});
// Commit. // Commit.
await page.mouse.click( await page.keyboard.press("Escape");
editorRect.x,
editorRect.y + 2 * editorRect.height
);
const ariaOwns = await page.$eval(".textLayer", el => { const ariaOwns = await page.$eval(".textLayer", el => {
const span = el.querySelector(`span[pdfjs="true"]`); const span = el.querySelector(`span[pdfjs="true"]`);
@ -254,13 +246,77 @@ describe("Editor", () => {
}) })
); );
}); });
it("must check that right click doesn't select", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const rect = await page.$eval(".annotationEditorLayer", el => {
const { x, y } = el.getBoundingClientRect();
return { x, y };
});
await page.keyboard.down("Control");
await page.keyboard.press("a");
await page.keyboard.up("Control");
await page.keyboard.down("Control");
await page.keyboard.press("Backspace");
await page.keyboard.up("Control");
const data = "Hello PDF.js World !!";
await page.mouse.click(rect.x + 100, rect.y + 100);
await page.type(`${editorPrefix}6 .internal`, data);
const editorRect = await page.$eval(`${editorPrefix}6`, el => {
const { x, y, width, height } = el.getBoundingClientRect();
return { x, y, width, height };
});
// Commit.
await page.keyboard.press("Escape");
expect(await getSelectedEditors(page))
.withContext(`In ${browserName}`)
.toEqual([6]);
await page.keyboard.press("Escape");
expect(await getSelectedEditors(page))
.withContext(`In ${browserName}`)
.toEqual([]);
await page.mouse.click(
editorRect.x + editorRect.width / 2,
editorRect.y + editorRect.height / 2
);
expect(await getSelectedEditors(page))
.withContext(`In ${browserName}`)
.toEqual([6]);
// Escape.
await page.keyboard.press("Escape");
expect(await getSelectedEditors(page))
.withContext(`In ${browserName}`)
.toEqual([]);
// TODO: uncomment that stuff once we've a way to dismiss
// the context menu.
/* await page.mouse.click(
editorRect.x + editorRect.width / 2,
editorRect.y + editorRect.height / 2,
{ button: "right" }
); */
})
);
});
}); });
describe("FreeText (multiselection)", () => { describe("FreeText (multiselection)", () => {
let pages; let pages;
beforeAll(async () => { beforeAll(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer");
}); });
afterAll(async () => { afterAll(async () => {

View File

@ -24,7 +24,7 @@ describe("Editor", () => {
let pages; let pages;
beforeAll(async () => { beforeAll(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer");
}); });
afterAll(async () => { afterAll(async () => {
@ -60,7 +60,7 @@ describe("Editor", () => {
expect(await getSelectedEditors(page)) expect(await getSelectedEditors(page))
.withContext(`In ${browserName}`) .withContext(`In ${browserName}`)
.toEqual([0, 2, 3]); .toEqual([0, 1, 2]);
await page.keyboard.press("Backspace"); await page.keyboard.press("Backspace");