Enforce PAGE-scrolling for *very* large/long documents (bug 1588435, PR 11263 follow-up)

This patch is essentially a continuation of PR 11263, which tried to improve loading/initialization performance of *very* large/long documents.

Note that browsers, in general, don't handle a huge amount of DOM-elements very well, with really poor (e.g. sluggish scrolling) performance once the number gets "large". Furthermore, at least in Firefox, it seems that DOM-elements towards the bottom of a HTML-page can effectively be ignored; for the PDF.js viewer that means that pages at the end of the document can become impossible to access.

Hence, in order to improve things for these *very* large/long documents, this patch will now enforce usage of the (recently added) PAGE-scrolling mode for these documents. As implemented, this will only happen once the number of pages *exceed* 15000 (which is hopefully rare in practice).
While this might feel a bit jarring to users being *forced* to use PAGE-scrolling, it seems all things considered like a better idea to ensure that the entire document actually remains accessible and with (hopefully) more acceptable performance.

Fixes [bug 1588435](https://bugzilla.mozilla.org/show_bug.cgi?id=1588435), to the extent that doing so is possible since the document contains 25560 pages (and is 197 MB large).
This commit is contained in:
Jonas Jenwald 2021-11-28 18:43:30 +01:00
parent f15eb63ed5
commit 6dfe4a9140
2 changed files with 35 additions and 4 deletions

View File

@ -54,6 +54,11 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js";
const DEFAULT_CACHE_SIZE = 10;
const PagesCountLimit = {
FORCE_SCROLL_MODE_PAGE: 15000,
FORCE_LAZY_PAGE_INIT: 7500,
};
/**
* @typedef {Object} PDFViewerOptions
* @property {HTMLDivElement} container - The container for the viewer element.
@ -515,6 +520,16 @@ class BaseViewer {
// Rendering (potentially) depends on this, hence fetching it immediately.
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
// Given that browsers don't handle huge amounts of DOM-elements very well,
// enforce usage of PAGE-scrolling when loading *very* long/large documents.
if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
console.warn(
"Forcing PAGE-scrolling for performance reasons, given the length of the document."
);
const mode = (this._scrollMode = ScrollMode.PAGE);
this.eventBus.dispatch("scrollmodechanged", { source: this, mode });
}
this._pagesCapability.promise.then(() => {
this.eventBus.dispatch("pagesloaded", {
source: this,
@ -618,7 +633,10 @@ class BaseViewer {
// In addition to 'disableAutoFetch' being set, also attempt to reduce
// resource usage when loading *very* long/large documents.
if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > 7500) {
if (
pdfDocument.loadingParams.disableAutoFetch ||
pagesCount > PagesCountLimit.FORCE_LAZY_PAGE_INIT
) {
// XXX: Printing is semi-broken with auto fetch disabled.
this._pagesCapability.resolve();
return;
@ -1685,6 +1703,9 @@ class BaseViewer {
if (!isValidScrollMode(mode)) {
throw new Error(`Invalid scroll mode: ${mode}`);
}
if (this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
return; // Disabled for performance reasons.
}
this._previousScrollMode = this._scrollMode;
this._scrollMode = mode;
@ -1957,4 +1978,4 @@ class BaseViewer {
}
}
export { BaseViewer, PDFPageViewBuffer };
export { BaseViewer, PagesCountLimit, PDFPageViewBuffer };

View File

@ -15,6 +15,7 @@
import { SCROLLBAR_PADDING, ScrollMode, SpreadMode } from "./ui_utils.js";
import { CursorTool } from "./pdf_cursor_tools.js";
import { PagesCountLimit } from "./base_viewer.js";
/**
* @typedef {Object} SecondaryToolbarOptions
@ -235,7 +236,7 @@ class SecondaryToolbar {
}
_bindScrollModeListener(buttons) {
function scrollModeChanged({ mode }) {
const scrollModeChanged = ({ mode }) => {
buttons.scrollPageButton.classList.toggle(
"toggled",
mode === ScrollMode.PAGE
@ -253,13 +254,22 @@ class SecondaryToolbar {
mode === ScrollMode.WRAPPED
);
// Permanently *disable* the Scroll buttons when PAGE-scrolling is being
// enforced for *very* long/large documents; please see the `BaseViewer`.
const forceScrollModePage =
this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE;
buttons.scrollPageButton.disabled = forceScrollModePage;
buttons.scrollVerticalButton.disabled = forceScrollModePage;
buttons.scrollHorizontalButton.disabled = forceScrollModePage;
buttons.scrollWrappedButton.disabled = forceScrollModePage;
// Temporarily *disable* the Spread buttons when horizontal scrolling is
// enabled, since the non-default Spread modes doesn't affect the layout.
const isScrollModeHorizontal = mode === ScrollMode.HORIZONTAL;
buttons.spreadNoneButton.disabled = isScrollModeHorizontal;
buttons.spreadOddButton.disabled = isScrollModeHorizontal;
buttons.spreadEvenButton.disabled = isScrollModeHorizontal;
}
};
this.eventBus._on("scrollmodechanged", scrollModeChanged);
this.eventBus._on("secondarytoolbarreset", evt => {