Skip to content

Fix crash with large grid and browser zoom #1052

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 25, 2025
Merged

Conversation

lukasmasuch
Copy link
Collaborator

@lukasmasuch lukasmasuch commented Jun 19, 2025

There is an issue with the infinity scroll optimization, which supports scroll areas that exceed the max height of divs (33.5 million pixels). If you have a large grid and you change the device pixel ratio (e.g., via zoom out), the grid allows overscrolling and eventually runs into an out-of-memory error (reproducible with the silly numbers demo in storybook)

Screen.Recording.2025-06-19.at.22.18.44.mov

This PR fixes this issue by improving the scroll optimization logic.

@lukasmasuch lukasmasuch requested a review from Copilot June 19, 2025 20:22
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR prevents out-of-memory errors with very large grids by clamping grid indices and improving the infinite scrolling logic to account for browser height limits and device pixel ratio changes.

  • Clamp cellY/cellBottom to valid row bounds in the grid scroller.
  • Track a virtual scroll position, reset on DPR (zoom) change, and map DOM scroll to full content height.
  • Split large padders into smaller segments to avoid browser div height limits.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/core/src/internal/scrolling-data-grid/scrolling-data-grid.tsx Clamps cellY and cellBottom to [0, rows) to prevent out‐of‐bounds row access.
packages/core/src/internal/scrolling-data-grid/infinite-scroller.tsx Adds virtualScrollY, DPR‐change effect, refined delta/edge scroll mapping, and segmented padders under max height.
Comments suppressed due to low confidence (2)

packages/core/src/internal/scrolling-data-grid/infinite-scroller.tsx:303

  • [nitpick] The variable h is ambiguous; rename it to something more descriptive like accumulatedHeight or remainingHeight for clarity.
    let h = 0;

packages/core/src/internal/scrolling-data-grid/infinite-scroller.tsx:154

  • Add tests to verify that virtualScrollY and lastScrollY are correctly reset when window.devicePixelRatio changes (e.g., simulate zoom in/out).
    React.useEffect(() => {

}

// Ensure virtual Y is within valid bounds
virtualY = Math.max(0, Math.min(virtualY, scrollHeight - cHeight));
virtualScrollY.current = virtualY; // Keep tracked position in bounds too
Copy link
Preview

Copilot AI Jun 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment duplicates updates inside the conditional branches; consider consolidating the update into a single place after clamping to avoid redundant writes.

Copilot uses AI. Check for mistakes.

Comment on lines +198 to +201
// This is a safeguard to prevent unexpected out-of-bounds access with large datasets
cellY = Math.max(0, Math.min(cellY, rows - 1));
cellBottom = Math.max(cellY, Math.min(cellBottom, rows));

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessary but an additional safeguard to prevent unexpected crashes.

@@ -256,9 +304,21 @@ export const InfiniteScroller: React.FC<Props> = p => {

let key = 0;
let h = 0;

// Browser's maximum div height limit
const maxDomHeight = BROWSER_MAX_DIV_HEIGHT;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just use BROWSER_MAX_DIV_HEIGHT directly

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed 👍

Copy link
Collaborator

@BrianHung BrianHung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -70,6 +70,9 @@ const ScrollRegionStyle = styled.div<{ isSafari: boolean }>`
}
`;

const BROWSER_MAX_DIV_HEIGHT = 33_554_432;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://stackoverflow.com/questions/16637530/whats-the-maximum-pixel-value-of-css-width-and-height-properties

Firefox: 33_554_400px
Chrome: 33_554_428px
Opera: 33_554_428px

Should we just use lowest / firefox constant?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reduced it to: 33_554_400 👍

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would test in firefox with storybook as a smoke test.

Copy link
Collaborator Author

@lukasmasuch lukasmasuch Jun 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too familiar with storybook smoke tests. Do we already have something like that in the GDG repo? Or do you mean just doing manual tests with with firefox and storybook?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meant manual in firefox for now!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good 👍 I did some additional manual testing with chrome, firefox and safari and everything seems to work fine. There is still a floating-point precision issue, which sometimes prevents the grid from scrolling to exactly the last row with very large grids. But this already existed in the previous version as well. I might try a follow-up fix for this as well.

@@ -70,6 +70,9 @@ const ScrollRegionStyle = styled.div<{ isSafari: boolean }>`
}
`;

const BROWSER_MAX_DIV_HEIGHT = 33_554_432;
const MAX_PADDER_SEGMENT_HEIGHT = 5_000_000;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an arbitrary constant under the BROWSER_MAX_DIV_HEIGHT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5_000_000 was the current value, I think it's mostly arbitrary... but not 100% sure about that. I added a bit more comment above MAX_PADDER_SEGMENT_HEIGHT to explain what it is actually used for.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it is arbitrary too, just to not have too many padders if we get to BROWSER_MAX_DIV_HEIGHT.

@lukasmasuch lukasmasuch merged commit ed43f13 into main Jun 25, 2025
8 checks passed
@lukasmasuch lukasmasuch deleted the fix/large-grid-dpr-crash branch June 25, 2025 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants