Skip to content

3.2.4 popup list flickers while scrolling on Chrome for macOS #46

Description

@hyiiiii

Summary / 摘要

On Chrome for macOS, Bookmarkie 3.2.4 (Chrome Web Store) flickers while scrolling the popup bookmark list. It is a popup compositing issue, not bookmark data corruption.

macOS 上 Chrome 商店版 3.2.4 的弹窗书签列表滚动时会闪烁。书签数据本身没有损坏。

This report is against the store 3.2.4 build. The public GitHub tree is still 2.3.3 (webpack popup) and does not contain the Vue virtual list used in 3.2.4, so I could not open a source PR against master. Happy to send a PR as soon as the 3.x source is public or you point at the correct branch/repo.

Environment

  • Chrome 151.0.7922.x on macOS (Apple Silicon, built-in display)
  • Bookmarkie 3.2.4 from the Chrome Web Store (ahlphbdcaacfhkiajebghpngknafklbj)
  • Reproduced in the toolbar popup, with a large bookmark tree

Repro

  1. Open the Bookmarkie popup.
  2. Expand enough folders that the list virtualizes and needs to scroll.
  3. Scroll the list.

Actual: the list flashes while scrolling and recovers when scrolling stops. After changing the virtual window from transform: translateY(...) to top, mid-list scrolling is stable, but the last screen still flashes until the last window is frozen and rubber-band overscroll is ignored.

Expected: the popup list scrolls without flashing.

Native chrome://bookmarks/ does not flash, so this is specific to the extension popup virtual list.

Cause

In the 3.2.4 popup bundle (BmTreeFlat), the virtual window is positioned every scroll frame with:

style: { transform: `translateY(${offset}px)` }

.tree-flat__window is already position: absolute; top: 0; left: 0; right: 0. Driving that layer with a GPU transform on every frame, plus a keyed re-render of the visible rows, flashes on Chrome / macOS popup compositing.

At the bottom of the list there is a second problem:

start = floor(scrollTop / rowHeight) - overscan
end = min(total, start + visible + overscan * 2)

Once end is clamped to total, start and the window offset keep moving (and macOS rubber-band can push scrollTop past the last row). That remounts the last slice and flashes only when you hit the end.

Suggested fix

These changes were verified against a local unpacked copy of 3.2.4. Bookmark data stays in Chrome's bookmark store.

1. Position the virtual window with layout top, not a composited transform

// before
style: { transform: `translateY(${offset}px)` }

// after
style: { top: `${offset}px` }

2. Freeze the last virtual window and clamp rubber-band scroll

const maxScroll = Math.max(0, totalHeight - viewportHeight)
const scrollForRange = Math.min(Math.max(0, scrollTop), maxScroll)

let start = Math.max(0, Math.floor(scrollForRange / rowHeight) - OVERSCAN)
let end = Math.min(total, start + visible + OVERSCAN * 2)
const windowSize = visible + OVERSCAN * 2

if (total > windowSize && end === total) {
  start = Math.max(0, total - windowSize)
}

3. CSS on the scroll container / window

.list-wrap {
  overflow-anchor: none;
  overscroll-behavior-y: none;
}
.tree-flat {
  overflow-anchor: none;
  contain: layout style;
}
.tree-flat__window {
  overflow-anchor: none;
}

Search, folder expand/collapse, editing, drag-and-drop, and the side panel can stay as they are.

Request

Could you apply this in the 3.2.x source and ship a store update? If the Vue source lives in another repo or a private branch, I can open a proper PR there. I did not open a PR against current master because that tree is still the 2.3.3 webpack popup and this patch would not apply.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions