Files
readest/apps/readest-app/.claude/memory/iframe-cross-realm-instanceof.md
T
Huang Xin 3a81e09911 fix(reader): scroll oversized blocks in-place instead of turning the page (#4400) (#4415)
Wide or tall tables, code blocks and display equations overflowed the reading
column and a scroll gesture over them turned the page instead of scrolling the
content (#4400).

- Wrap tables and display equations in a horizontally/vertically scrollable
  container; route touch + wheel along the box's scrollable axis so it scrolls
  the box and never turns the page, even at the edge (both axes).
- A box that fits its column is marked fit (overflow:visible) so it never clips
  or captures gestures; the fit decision is measured once after layout via a
  self-disconnecting ResizeObserver, so it never relayerizes during a page turn.
- The scroll wrapper carries a new cfi-skip attribute that makes it transparent
  to CFI: epubcfi.js hoists a cfi-skip node's children into its parent (unlike
  cfi-inert which drops the subtree), and xcfi.ts mirrors this for CFI<->XPointer
  so existing highlights, bookmarks and KOSync positions inside a wrapped table
  or equation still resolve. The sanitizer whitelists cfi-skip.
- Bump foliate-js submodule (cfi-skip support + raf fallback for large sections).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:45:18 +02:00

1.3 KiB

name, description, metadata
name description metadata
iframe-cross-realm-instanceof App-bundle code handling foliate iframe DOM must not use `instanceof Element/HTMLElement` — cross-realm, always false
node_type type originSessionId
memory project 2e8d274e-a4da-4d63-8afb-d7a600d560b2

Reader content lives in foliate iframes, each with its own JS realm. App-bundle code (e.g. src/utils/style.ts, iframeEventHandlers.ts) runs in the top window realm, so its Element/HTMLElement constructors differ from the iframe's.

someIframeEl instanceof Element (top-realm Element) is always false — verified: Element === iframeWin.Element is false. So any guard like if (!(target instanceof Element)) return silently drops every real iframe element. Functions still pass jsdom unit tests (single realm) yet are dead in the running app.

Use duck-typing instead: if (!target || !('closest' in target)) return; then (target as Element).closest(...). For node-type checks use node.nodeType === Node.ELEMENT_NODE (numeric constant, realm-safe) or check classList/closest presence.

Seen in PR #4391 (wide-table horizontal scroll): the touch findWrapper and applyTableStyle's re-entrancy guard both used instanceof, so the touch routing never fired and the dedupe guard never tripped. Related: foliate-touch-listener-capture-phase.