Keep the Filters popover's height budget current with the visual viewport

place() already budgets against the smaller of documentElement.clientHeight and
visualViewport.height, because a fixed element is positioned against the layout
viewport while iOS shrinks the visual one behind its chrome. But the open panel
was re-placed only on window resize and on #diff-header scroll, and neither of
those fires when the visual viewport alone changes - an expanding toolbar or the
on-screen keyboard does exactly that. The cap then stays at the height it was
computed with and the bottom filters go back behind the chrome, which is the
failure the cap was added to prevent.

That half of place() was recorded as unverifiable, on the grounds that Chromium
has no dynamic toolbar and the two viewports are always equal here. They are
equal only at page scale 1. CDP Emulation.setPageScaleFactor is pinch-zoom: it
shrinks the visual viewport against a layout viewport that does not move, and
fires VisualViewport resize - the same split iOS produces. So both halves are
measurable, and the harness now measures them at 390x844@2, 844x390@1.5 and
390x390@1.5:

  * placing while the visual viewport is already the smaller one caps the panel
    to it - 85.6px against a layout-budget 215.6px at 844x390 - which is what
    shows the Math.min binds rather than sitting inert;
  * on the tree without this commit, shrinking the viewport under an open panel
    left the cap untouched and the panel overhung the visible area by 122px at
    both short viewports and 46.8px at 390x844. Three failures, one per
    viewport, and none anywhere else in the harness.

The probe asserts its own instrument before its result: unless the scale really
splits the two heights and the page really receives the resize event, it fails
rather than passes. It also fails if no viewport in the set caps more tightly
than the layout height would have, since then the visual-viewport budget is
untested, and if a viewport's panel already fitted before the shrink, since then
it cannot detect a stale cap.

The re-placed cap lands on the same value as placing fresh at that viewport
(210.4, 85.6, 65.0px), and the panel's width is unchanged by the re-placement.

Co-authored-by: Jeff Hedlund <jhedlund@gmail.com>
Signed-off-by: Jeff Hedlund <jhedlund@gmail.com>
This commit is contained in:
Engineer
2026-09-11 08:01:49 -04:00
co-authored by Jeff Hedlund
parent 9a5012267a
commit 01420ac7eb
@@ -200,6 +200,21 @@ function setupDiffFilters() {
place();
}
}, {passive: true});
// place() budgets against the *visual* viewport when it is the smaller of
// the two, and on iOS that one can shrink on its own - a toolbar expanding
// or the on-screen keyboard coming up moves it without resizing the layout
// viewport, so no window resize fires. Without this the cap stays at the
// height it was placed with and the bottom rows sit behind the chrome
// again. Measured in Chromium via CDP pinch-zoom, which splits the two
// viewports the same way: the panel kept a 195px cap against a viewport
// that had become 260px tall and overhung it by 122px.
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', function () {
if (isOpen()) {
place();
}
});
}
window.addEventListener('hashchange', function () {
close(false);
});