Skip to content

TextLayerBuilder: global selection listeners are never re-bound after the viewer that registered them is aborted while another text layer is alive #22129

Description

@JDL747

Version: pdfjs-dist 6.3.289 (same code on master, web/text_layer_builder.js, checked against v6.4.299)

Setup: two PDFViewer instances on one page, each created with its own abortSignal (for example a page reader plus a modal reader).

Steps

  1. Mount viewer A with abortSignal: a.signal and render a page with a text layer.
  2. Mount viewer B with abortSignal: b.signal and render a page with a text layer.
  3. Abort a (viewer A is torn down; viewer B stays).
  4. In viewer B, press the mouse on the text layer, drag, release.

Expected: the selection works, and on pointerup the text layer drops its selecting class.

Actual: after the first mousedown, the text layer keeps selecting. With the stock CSS, .textLayer.selecting .endOfContent { top: 0 } makes endOfContent cover the whole layer (user-select: none), and .textLayer.selecting ~ .annotationLayer section { pointer-events: none } disables the link annotations. New drag selections and links on that page stop working until every text layer is gone.

Cause: #enableGlobalSelectionListener(globalAbortSignal) binds the document listeners (pointerdown, pointerup, blur, keyup, selectionchange) on AbortSignal.any([#selectionChangeAC.signal, globalAbortSignal]), where globalAbortSignal is the signal of the FIRST viewer that registered a text layer. It returns early while #selectionChangeAC is set, and #selectionChangeAC is cleared only when #textLayers.size === 0. When the first viewer's signal aborts, the listeners go away, but #selectionChangeAC stays set because viewer B's text layer is still in #textLayers, so no later call binds them again.

Possible fix: the shared listeners should not live on one viewer's signal. For example, keep each text layer's abortSignal beside its endOfContent in #textLayers; when the signal the listeners were bound with aborts, drop that viewer's layers, clear #selectionChangeAC, and if text layers remain, bind again with a remaining layer's signal. Checking only #selectionChangeAC's own signal at the next #bindMouse is not enough: the surviving viewer's existing layers never call #bindMouse again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions