Skip to content

Commit

Permalink
[Editor] Make the text layer focusable before the editors (bug 1881746)
Browse files Browse the repository at this point in the history
Keep the different layers in a constant order to avoid the use of a z-index
and a tab-index.
  • Loading branch information
calixteman committed Mar 12, 2024
1 parent e647311 commit 059f93e
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 20 deletions.
4 changes: 4 additions & 0 deletions src/display/editor/annotation_editor_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class AnnotationEditorLayer {
* editor creation.
*/
enable() {
this.div.tabIndex = 0;
this.togglePointerEvents(true);
const annotationElementIds = new Set();
for (const editor of this.#editors.values()) {
Expand Down Expand Up @@ -274,6 +275,7 @@ class AnnotationEditorLayer {
*/
disable() {
this.#isDisabling = true;
this.div.tabIndex = -1;
this.togglePointerEvents(false);
const hiddenAnnotationIds = new Set();
for (const editor of this.#editors.values()) {
Expand Down Expand Up @@ -333,6 +335,7 @@ class AnnotationEditorLayer {
}

enableTextSelection() {
this.div.tabIndex = -1;
if (this.#textLayer?.div) {
this.#textLayer.div.addEventListener(
"pointerdown",
Expand All @@ -343,6 +346,7 @@ class AnnotationEditorLayer {
}

disableTextSelection() {
this.div.tabIndex = 0;
if (this.#textLayer?.div) {
this.#textLayer.div.removeEventListener(
"pointerdown",
Expand Down
10 changes: 9 additions & 1 deletion src/display/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ class AnnotationEditor {
this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
this.div.className = this.name;
this.div.setAttribute("id", this.id);
this.div.setAttribute("tabIndex", 0);
this.div.tabIndex = 0;
if (!this._isVisible) {
this.div.classList.add("hidden");
}
Expand Down Expand Up @@ -1680,6 +1680,14 @@ class AnnotationEditor {
this.div.classList.toggle("hidden", !visible);
this._isVisible = visible;
}

enable() {
this.div.tabIndex = 0;
}

disable() {
this.div.tabIndex = -1;
}
}

// This class is used to fake an editor which has been deleted.
Expand Down
6 changes: 6 additions & 0 deletions src/display/editor/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,9 @@ class AnnotationEditorUIManager {
for (const layer of this.#allLayers.values()) {
layer.enable();
}
for (const editor of this.#allEditors.values()) {
editor.enable();
}
}
}

Expand All @@ -1562,6 +1565,9 @@ class AnnotationEditorUIManager {
for (const layer of this.#allLayers.values()) {
layer.disable();
}
for (const editor of this.#allEditors.values()) {
editor.disable();
}
}
}

Expand Down
50 changes: 50 additions & 0 deletions test/integration/highlight_editor_spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
getSerialized,
kbBigMoveLeft,
kbBigMoveUp,
kbFocusNext,
kbFocusPrevious,
kbSelectAll,
loadAndWait,
scrollIntoView,
Expand Down Expand Up @@ -1510,4 +1512,52 @@ describe("Highlight Editor", () => {
);
});
});

describe("Text layer must have the focus before highlights", () => {
let pages;

beforeAll(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});

afterAll(async () => {
await closePages(pages);
});

it("must check the focus order", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.click("#editorHighlight");
await page.waitForSelector(".annotationEditorLayer.highlightEditing");

let rect = await getSpanRectFromText(page, 1, "Abstract");
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(getEditorSelector(0));

rect = await getSpanRectFromText(page, 1, "Languages");
x = rect.x + rect.width / 2;
y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(getEditorSelector(1));
await page.focus(getEditorSelector(1));

await kbFocusPrevious(page);
await page.waitForFunction(
sel => document.querySelector(sel) === document.activeElement,
{},
`.page[data-page-number="1"] > .textLayer`
);

await kbFocusNext(page);
await page.waitForFunction(
sel => document.querySelector(sel) === document.activeElement,
{},
getEditorSelector(1)
);
})
);
});
});
});
20 changes: 20 additions & 0 deletions test/integration/test_utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,24 @@ async function kbDeleteLastWord(page) {
}
}

async function kbFocusNext(page) {
const handle = await createPromise(page, resolve => {
window.addEventListener("focusin", resolve, { once: true });
});
await page.keyboard.press("Tab");
await awaitPromise(handle);
}

async function kbFocusPrevious(page) {
const handle = await createPromise(page, resolve => {
window.addEventListener("focusin", resolve, { once: true });
});
await page.keyboard.down("Shift");
await page.keyboard.press("Tab");
await page.keyboard.up("Shift");
await awaitPromise(handle);
}

export {
awaitPromise,
clearInput,
Expand All @@ -484,6 +502,8 @@ export {
kbBigMoveUp,
kbCopy,
kbDeleteLastWord,
kbFocusNext,
kbFocusPrevious,
kbGoToBegin,
kbGoToEnd,
kbModifierDown,
Expand Down
1 change: 0 additions & 1 deletion web/annotation_editor_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@
font-size: calc(100px * var(--scale-factor));
transform-origin: 0 0;
cursor: auto;
z-index: 4;
}

.annotationEditorLayer.waiting {
Expand Down
7 changes: 2 additions & 5 deletions web/annotation_editor_layer_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import { GenericL10n } from "web-null_l10n";
/**
* @typedef {Object} AnnotationEditorLayerBuilderOptions
* @property {AnnotationEditorUIManager} [uiManager]
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {IL10n} [l10n]
* @property {TextAccessibilityManager} [accessibilityManager]
Expand All @@ -52,7 +51,6 @@ class AnnotationEditorLayerBuilder {
* @param {AnnotationEditorLayerBuilderOptions} options
*/
constructor(options) {
this.pageDiv = options.pageDiv;
this.pdfPage = options.pdfPage;
this.accessibilityManager = options.accessibilityManager;
this.l10n = options.l10n;
Expand All @@ -72,7 +70,7 @@ class AnnotationEditorLayerBuilder {
* @param {PageViewport} viewport
* @param {string} intent (default value is 'display')
*/
async render(viewport, intent = "display") {
async render(viewport, view, intent = "display") {
if (intent !== "display") {
return;
}
Expand All @@ -91,10 +89,9 @@ class AnnotationEditorLayerBuilder {
// Create an AnnotationEditor layer div
const div = (this.div = document.createElement("div"));
div.className = "annotationEditorLayer";
div.tabIndex = 0;
div.hidden = true;
div.dir = this.#uiManager.direction;
this.pageDiv.append(div);
view.addLayer(div, "annotationEditorLayer");

this.annotationEditorLayer = new AnnotationEditorLayer({
uiManager: this.#uiManager,
Expand Down
1 change: 0 additions & 1 deletion web/annotation_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
left: 0;
pointer-events: none;
transform-origin: 0 0;
z-index: 3;

&[data-main-rotation="90"] .norotate {
transform: rotate(270deg) translateX(-100%);
Expand Down
7 changes: 2 additions & 5 deletions web/annotation_layer_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { PresentationModeState } from "./ui_utils.js";

/**
* @typedef {Object} AnnotationLayerBuilderOptions
* @property {HTMLDivElement} pageDiv
* @property {PDFPageProxy} pdfPage
* @property {AnnotationStorage} [annotationStorage]
* @property {string} [imageResourcesPath] - Path for image resources, mainly
Expand All @@ -51,7 +50,6 @@ class AnnotationLayerBuilder {
* @param {AnnotationLayerBuilderOptions} options
*/
constructor({
pageDiv,
pdfPage,
linkService,
downloadManager,
Expand All @@ -64,7 +62,6 @@ class AnnotationLayerBuilder {
annotationCanvasMap = null,
accessibilityManager = null,
}) {
this.pageDiv = pageDiv;
this.pdfPage = pdfPage;
this.linkService = linkService;
this.downloadManager = downloadManager;
Expand All @@ -89,7 +86,7 @@ class AnnotationLayerBuilder {
* @returns {Promise<void>} A promise that is resolved when rendering of the
* annotations is complete.
*/
async render(viewport, intent = "display") {
async render(viewport, view, intent = "display") {
if (this.div) {
if (this._cancelled || !this.annotationLayer) {
return;
Expand All @@ -115,7 +112,7 @@ class AnnotationLayerBuilder {
// if there is at least one annotation.
const div = (this.div = document.createElement("div"));
div.className = "annotationLayer";
this.pageDiv.append(div);
view.addLayer(div, "annotationLayer");

if (annotations.length === 0) {
this.hide();
Expand Down
38 changes: 33 additions & 5 deletions web/pdf_page_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ class PDFPageView {

#viewportMap = new WeakMap();

static #layerOrder = new Map([
["canvasWrapper", 0],
["textLayer", 1],
["annotationLayer", 2],
["annotationEditorLayer", 3],
["xfaLayer", 2],
]);

#layers = [null, null, null, null];

/**
* @param {PDFPageViewOptions} options
*/
Expand Down Expand Up @@ -223,6 +233,19 @@ class PDFPageView {
}
}

addLayer(div, name) {
const pos = PDFPageView.#layerOrder.get(name);
this.#layers[pos] = div;
for (let i = pos - 1; i >= 0; i--) {
const layer = this.#layers[i];
if (layer) {
layer.after(div);
return;
}
}
this.div.prepend(div);
}

get renderingState() {
return this.#renderingState;
}
Expand Down Expand Up @@ -337,7 +360,7 @@ class PDFPageView {
async #renderAnnotationLayer() {
let error = null;
try {
await this.annotationLayer.render(this.viewport, "display");
await this.annotationLayer.render(this.viewport, this, "display");
} catch (ex) {
console.error(`#renderAnnotationLayer: "${ex}".`);
error = ex;
Expand All @@ -353,7 +376,7 @@ class PDFPageView {
async #renderAnnotationEditorLayer() {
let error = null;
try {
await this.annotationEditorLayer.render(this.viewport, "display");
await this.annotationEditorLayer.render(this.viewport, this, "display");
} catch (ex) {
console.error(`#renderAnnotationEditorLayer: "${ex}".`);
error = ex;
Expand Down Expand Up @@ -392,7 +415,7 @@ class PDFPageView {
if (this.xfaLayer?.div) {
// Pause translation when inserting the xfaLayer in the DOM.
this.l10n.pause();
this.div.append(this.xfaLayer.div);
this.addLayer(this.xfaLayer.div, "xfaLayer");
this.l10n.resume();
}

Expand Down Expand Up @@ -531,6 +554,10 @@ class PDFPageView {
continue;
}
node.remove();
const indexLayer = this.#layers.indexOf(node);
if (indexLayer >= 0) {
this.#layers[indexLayer] = null;
}
}
div.removeAttribute("data-loaded");

Expand Down Expand Up @@ -877,7 +904,8 @@ class PDFPageView {
// overflow will be hidden in Firefox.
const canvasWrapper = document.createElement("div");
canvasWrapper.classList.add("canvasWrapper");
div.append(canvasWrapper);
canvasWrapper.setAttribute("aria-hidden", true);
this.addLayer(canvasWrapper, "canvasWrapper");

if (
!this.textLayer &&
Expand All @@ -895,7 +923,7 @@ class PDFPageView {
this.textLayer.onAppend = textLayerDiv => {
// Pause translation when inserting the textLayer in the DOM.
this.l10n.pause();
this.div.append(textLayerDiv);
this.addLayer(textLayerDiv, "textLayer");
this.l10n.resume();
};
}
Expand Down
1 change: 0 additions & 1 deletion web/pdf_viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
overflow: hidden;
width: 100%;
height: 100%;
z-index: 1;
}

.pdfViewer .page {
Expand Down
1 change: 0 additions & 1 deletion web/text_layer_builder.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
text-size-adjust: none;
forced-color-adjust: none;
transform-origin: 0 0;
z-index: 2;
caret-color: CanvasText;

&.highlighting {
Expand Down
1 change: 1 addition & 0 deletions web/text_layer_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class TextLayerBuilder {
this.onAppend = null;

this.div = document.createElement("div");
this.div.tabIndex = 0;
this.div.className = "textLayer";
}

Expand Down

0 comments on commit 059f93e

Please sign in to comment.