Skip to content

Commit

Permalink
Merge pull request #17611 from calixteman/caret_browsing_mode
Browse files Browse the repository at this point in the history
Implement caret browsing mode (bug 807730)
  • Loading branch information
calixteman authored Feb 7, 2024
2 parents 7d9cc49 + 81466ee commit 60fd9d5
Show file tree
Hide file tree
Showing 5 changed files with 486 additions and 0 deletions.
1 change: 1 addition & 0 deletions test/integration-boot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async function runTests(results) {
spec_files: [
"accessibility_spec.mjs",
"annotation_spec.mjs",
"caret_browsing_spec.mjs",
"copy_paste_spec.mjs",
"find_spec.mjs",
"freetext_editor_spec.mjs",
Expand Down
100 changes: 100 additions & 0 deletions test/integration/caret_browsing_spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* Copyright 2021 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { closePages, loadAndWait } from "./test_utils.mjs";

const waitForSelectionChange = (page, selection) =>
page.waitForFunction(
// We need to replace EOL on Windows to make the test pass.
sel => window.getSelection().toString().replaceAll("\r\n", "\n") === sel,
{},
selection
);

describe("Caret browsing", () => {
describe("Selection", () => {
let pages;

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

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

it("must move the caret down and check the selection", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const spanRect = await page.evaluate(() => {
const span = document.querySelector(
`.page[data-page-number="1"] > .textLayer > span`
);
const { x, y, width, height } = span.getBoundingClientRect();
return { x, y, width, height };
});
await page.mouse.click(
spanRect.x + 1,
spanRect.y + spanRect.height / 2,
{ count: 2 }
);
await page.keyboard.down("Shift");
for (let i = 0; i < 6; i++) {
await page.keyboard.press("ArrowRight");
}
await page.keyboard.up("Shift");
await waitForSelectionChange(page, "Trace-based");

await page.keyboard.down("Shift");
await page.keyboard.press("ArrowDown");
await page.keyboard.up("Shift");

// The caret is just before Languages.
await waitForSelectionChange(
page,
"Trace-based Just-in-Time Type Specialization for Dynamic\n"
);

await page.keyboard.down("Shift");
await page.keyboard.press("ArrowDown");
await page.keyboard.up("Shift");

// The caret is just before Mike Shaver.
await waitForSelectionChange(
page,
"Trace-based Just-in-Time Type Specialization for Dynamic\nLanguages\nAndreas Gal∗+, Brendan Eich∗, "
);

await page.keyboard.down("Shift");
await page.keyboard.press("ArrowUp");
await page.keyboard.up("Shift");

// The caret is just before Languages.
await waitForSelectionChange(
page,
"Trace-based Just-in-Time Type Specialization for Dynamic\n"
);

await page.keyboard.down("Shift");
await page.keyboard.press("ArrowUp");
await page.keyboard.up("Shift");

// The caret is in the middle of Time.
await waitForSelectionChange(page, "Trace-based Just-in-Tim");
})
);
});
});
});
51 changes: 51 additions & 0 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { AutomationEventBus, EventBus } from "./event_utils.js";
import { LinkTarget, PDFLinkService } from "./pdf_link_service.js";
import { AltTextManager } from "web-alt_text_manager";
import { AnnotationEditorParams } from "web-annotation_editor_params";
import { CaretBrowsingMode } from "./caret_browsing.js";
import { DownloadManager } from "web-download_manager";
import { ExternalServices } from "web-external_services";
import { OverlayManager } from "./overlay_manager.js";
Expand Down Expand Up @@ -162,6 +163,7 @@ const PDFViewerApplication = {
_touchInfo: null,
_isCtrlKeyDown: false,
_nimbusDataPromise: null,
_caretBrowsing: null,

// Called once when the document is loaded.
async initialize(appConfig) {
Expand Down Expand Up @@ -774,6 +776,23 @@ const PDFViewerApplication = {
);
},

get supportsCaretBrowsingMode() {
return shadow(
this,
"supportsCaretBrowsingMode",
AppOptions.get("supportsCaretBrowsingMode")
);
},

moveCaret(isUp, select) {
this._caretBrowsing ||= new CaretBrowsingMode(
this.appConfig.mainContainer,
this.appConfig.viewerContainer,
this.appConfig.toolbar?.container
);
this._caretBrowsing.moveCaret(isUp, select);
},

initPassiveLoading(file) {
if (
typeof PDFJSDev === "undefined" ||
Expand Down Expand Up @@ -3014,6 +3033,15 @@ function webViewerKeyDown(evt) {
turnOnlyIfPageFit = false;
switch (evt.keyCode) {
case 38: // up arrow
if (PDFViewerApplication.supportsCaretBrowsingMode) {
PDFViewerApplication.moveCaret(
/* isUp = */ true,
/* select = */ false
);
handled = true;
break;
}
/* falls through */
case 33: // pg up
// vertical scrolling using arrow/pg keys
if (pdfViewer.isVerticalScrollbarEnabled) {
Expand All @@ -3028,6 +3056,9 @@ function webViewerKeyDown(evt) {
turnPage = -1;
break;
case 37: // left arrow
if (PDFViewerApplication.supportsCaretBrowsingMode) {
return;
}
// horizontal scrolling using arrow keys
if (pdfViewer.isHorizontalScrollbarEnabled) {
turnOnlyIfPageFit = true;
Expand All @@ -3051,6 +3082,15 @@ function webViewerKeyDown(evt) {
}
break;
case 40: // down arrow
if (PDFViewerApplication.supportsCaretBrowsingMode) {
PDFViewerApplication.moveCaret(
/* isUp = */ false,
/* select = */ false
);
handled = true;
break;
}
/* falls through */
case 34: // pg down
// vertical scrolling using arrow/pg keys
if (pdfViewer.isVerticalScrollbarEnabled) {
Expand All @@ -3066,6 +3106,9 @@ function webViewerKeyDown(evt) {
turnPage = 1;
break;
case 39: // right arrow
if (PDFViewerApplication.supportsCaretBrowsingMode) {
return;
}
// horizontal scrolling using arrow keys
if (pdfViewer.isHorizontalScrollbarEnabled) {
turnOnlyIfPageFit = true;
Expand Down Expand Up @@ -3139,6 +3182,14 @@ function webViewerKeyDown(evt) {
handled = true;
break;

case 38: // up arrow
PDFViewerApplication.moveCaret(/* isUp = */ true, /* select = */ true);
handled = true;
break;
case 40: // down arrow
PDFViewerApplication.moveCaret(/* isUp = */ false, /* select = */ true);
handled = true;
break;
case 82: // 'r'
PDFViewerApplication.rotatePages(-90);
break;
Expand Down
5 changes: 5 additions & 0 deletions web/app_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ const defaultOptions = {
value: false,
kind: OptionKind.BROWSER,
},
supportsCaretBrowsingMode: {
/** @type {boolean} */
value: false,
kind: OptionKind.BROWSER,
},
supportsDocumentFonts: {
/** @type {boolean} */
value: true,
Expand Down
Loading

0 comments on commit 60fd9d5

Please sign in to comment.