Skip to content

Timeline renders blank in Firefox with large libraries — virtual scroller exceeds Firefox's max element size #1715

Description

@CR0CKER

Describe the bug

On a large library, the Timeline in Firefox renders the date scrubber and top matter but no photos at all — a completely empty grid. Every other view works (Folders, Favourites), the same account renders perfectly in Safari on iOS, and the server is healthy: /api/days and /api/days/<id> return 200 with full payloads, and preview requests return real JPEGs. Nothing appears in the JS console and nothing in the Nextcloud log.

The cause seems to be that RecycleScroller sizes a single element to the height of the entire library, and Firefox apparently has a hard maximum element size of 17,187,496 px. Critically, when that is exceeded Firefox resets the element's height to 0 rather than clamping it (see Mozilla bug 1527883, open since 2019, P5) — so the container collapses and the timeline goes completely blank instead of merely truncating. At least that's what seems to be the issue.

With 371,913 photos across 5,568 days, I measured the wrapper Memories asks for:

Grid width Columns .vue-recycle-scroller__item-wrapper min-height vs 17,187,496 px
1055 px 4 22,254,500 px over by 29% — blank
1468 px 6 17,982,000 px over by 4.6% — blank
1468 px @ 90% page zoom 7 under the cap renders correctly

Zooming the page to 90% and hard-reloading creates a seventh column, drops the total under the limit, and the timeline works.

Relevant code on master (v8.1.0):

  • src/components/Timeline.vue:28-42 — rows go into RecycleScroller (vue-virtual-scroller 1.1.2), which sets min-height: <totalSize>px on one .vue-recycle-scroller__item-wrapper and positions rows with translateY(). The library has no mitigation for the browser cap.
  • src/components/Timeline.vue:137,482-483 — desktop rows are a fixed DESKTOP_ROW_HEIGHT = 200 with numCols = Math.ceil(rowWidth / (rowHeight * 4/3)), so total height scales as photos / numCols * 200. On a HiDPI laptop the browser sees ~1280 CSS px, which caps you at 5–6 columns — widening the window cannot buy enough.

Steps To Reproduce

  1. Have a timeline of roughly 350k+ photos (the threshold is a height, not a count: it trips when photos / numCols * 200 + days * 40 exceeds ~17.19M px).
  2. Open the Timeline in Firefox at 100% zoom in a window where the grid is ≤ ~1500 px wide.
  3. The grid is empty. The scrubber, top matter and sidebar all render normally.
  4. In the console:
    const r = document.querySelector('.recycler');
    const h = parseInt(r.querySelector('.vue-recycle-scroller__item-wrapper').style.minHeight);
    console.log(r.clientWidth, Math.ceil(r.clientWidth / 266.67), h, h > 17187496 ? 'OVER' : 'under');
    Anything over 17,187,496 renders blank.
  5. Press Ctrl+- to 90%, then hard-reload (Ctrl+Shift+R) — photos appear.

The hard reload is required. Timeline.vue:848 does nrows = prevRows?.length || nrows ("learn from existing rows"), so an already-built list keeps the row count from whatever width it was first built at. Changing zoom or window size without reloading leaves the old, larger height in place and makes the measurement misleading.

Platform

  • OS: Armbian 24.8.1 (bookworm), aarch64 — Raspberry Pi 5 / NextcloudPi
  • Browser: Firefox 153 (via Zen Browser) on Linux. Not reproducible in Chromium or iOS Safari, whose caps are 33,554,428 px and higher respectively.
  • Memories Version: 7.8.2 — the latest release compatible with Nextcloud 32 (info.xml for 7.8.2 declares max-version="32"; 8.x declares min-version="33"), so this is "latest" for this platform. The relevant code is nonetheless unchanged on master at v8.1.0: DESKTOP_ROW_HEIGHT = 200 (Timeline.vue:137), the numCols formula (:482-483), the RecycleScroller block (:28-42) and the sticky-row line (:848) are all identical, and vue-virtual-scroller is still pinned at 1.1.2. This affects v8.1.0.
  • Nextcloud Version: 32.0.12
  • PHP Version: 8.3.28

Additional context

  • Any errors in the JS console? None. That is what makes this expensive to diagnose — it is indistinguishable from a server-side failure. I checked the index (Indexing completed successfully, 471,239 rows), timelinePath, the Apache access log (all 200s), the Nextcloud error log (nothing), and the browser's CacheStorage (589 cached day responses present) before thinking to measure the DOM.
  • Any errors in the Nextcloud server logs? None.

Suggested fixes, cheapest first:

  1. Fail loudly instead of silently. If totalSize exceeds ~17.1M px on Gecko, log a console warning or show a message. This alone would turn a multi-hour investigation into a five-second one, and it is a few lines.
  2. Auto-fit. When the computed total would exceed the cap, increase numCols (or reduce rowHeight) until it fits. Denser thumbnails are a far better failure mode than a blank page, and it needs no user action.
  3. Proper fix — scale the scroll coordinate space. Cap the container at a safe height and map virtual offsets onto it, the approach react-virtualized takes with its maxScrollSize / scaling position manager for exactly this browser limit. This needs either a patch to vue-virtual-scroller (which has no handling for it) or a wrapper in Memories.

Happy to test patches against this library — it reproduces 100% of the time and sits right on the boundary, so it is a good regression case. (Testing would be on 7.8.2, since 8.x needs Nextcloud 33 and this instance is on 32; the affected code is identical between the two.)

Activity

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

    bugSomething isn't workingperformanceIssues related to performance

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions