mirror of
https://github.com/got-feedBack/feedBack-desktop.git
synced 2026-09-10 23:04:09 +00:00
fix(panes): only the main window may drive the tray; clamp fallback defaults
1. pane:sync was accepted from ANY renderer with the preload bridge. Pane windows are same-origin top-level frames, so preload's isMainFrame gate hands them the bridge too — which means a pane window (or any allowed pop-up) could send pane:sync and overwrite the tray's registry, most simply by pushing an empty list and emptying the menu. Exactly one renderer owns the pane registry. It is now accepted from that one only: event.sender must be the main window's webContents. 2. sanitizeWindowBounds returned sizing.defaultWidth/Height unclamped. The min clamp only runs when `saved` parses, so a caller whose defaults undercut its own minimums would get a window below the floor on precisely the paths where nothing is saved — first launch, or a corrupt config — and a correctly sized one everywhere else. That is the worst shape a bug can have: invisible in the common case, and visible only to a new user. The fallback is clamped to the floor now, with a test. window-bounds: 14/14. Signed-off-by: topkoa <topkoa@gmail.com>
This commit is contained in:
+12
-1
@@ -261,7 +261,18 @@ export function initPaneHosts(deps: { getMainWindow: () => BrowserWindow | null
|
||||
// The renderer pushes its registry whenever a pane is registered, opened or
|
||||
// closed, so the tray can list panes it otherwise knows nothing about.
|
||||
// Fire-and-forget: the tray is a view of the renderer's truth.
|
||||
ipcMain.on(IPC_PANE_SYNC, (_event, panes: unknown) => {
|
||||
//
|
||||
// Only the MAIN window's truth, though. Pane windows are same-origin top-level
|
||||
// frames, so preload.ts's isMainFrame gate gives them the bridge too — meaning a
|
||||
// pane window (or any allowed pop-up) could send pane:sync and overwrite the
|
||||
// tray's registry, most simply by pushing an empty list and emptying the menu.
|
||||
// Only one renderer owns the pane registry; accept it from that one only.
|
||||
ipcMain.on(IPC_PANE_SYNC, (event, panes: unknown) => {
|
||||
const main = getMainWindow();
|
||||
if (!main || main.isDestroyed() || event.sender !== main.webContents) {
|
||||
console.warn('[panes] ignoring pane:sync from a webContents that is not the main window');
|
||||
return;
|
||||
}
|
||||
lastSync = Array.isArray(panes)
|
||||
? panes.filter((p): p is TrayPane => !!p && typeof p.id === 'string' && typeof p.title === 'string')
|
||||
: [];
|
||||
|
||||
@@ -67,7 +67,18 @@ export function sanitizeWindowBounds(
|
||||
displays: DisplayRect[],
|
||||
sizing: WindowSizing = MAIN_WINDOW_SIZING,
|
||||
): RestoredWindowBounds {
|
||||
const defaults: RestoredWindowBounds = { width: sizing.defaultWidth, height: sizing.defaultHeight, maximized: false };
|
||||
// The floor applies to the FALLBACK too, not just to saved bounds.
|
||||
//
|
||||
// The min clamp below only runs when `saved` parses. So a caller whose defaults
|
||||
// are smaller than its own minimums would get a window under the floor on
|
||||
// exactly the paths where nothing is saved — first launch, or a corrupt config —
|
||||
// and a perfectly sized one everywhere else. That is the worst shape a bug can
|
||||
// have: invisible in the common case, and only in front of a new user.
|
||||
const defaults: RestoredWindowBounds = {
|
||||
width: Math.max(sizing.defaultWidth, sizing.minWidth),
|
||||
height: Math.max(sizing.defaultHeight, sizing.minHeight),
|
||||
maximized: false,
|
||||
};
|
||||
if (displays.length === 0) return defaults;
|
||||
|
||||
const b = saved as SavedWindowBounds | undefined;
|
||||
|
||||
@@ -95,3 +95,18 @@ test('omitting sizing keeps the main window behaviour exactly as before', () =>
|
||||
const out = sanitizeWindowBounds({ x: 10, y: 10, width: 50, height: 20 }, [PRIMARY]);
|
||||
assert.deepEqual(out, { x: 10, y: 10, width: MIN_WIDTH, height: MIN_HEIGHT, maximized: false });
|
||||
});
|
||||
|
||||
test('custom sizing: defaults below the configured minimum are clamped up', () => {
|
||||
// A caller whose defaults undercut its own floor. The min clamp only runs on
|
||||
// saved bounds, so without clamping the fallback too, the floor would hold
|
||||
// everywhere EXCEPT first launch and a corrupt config — i.e. only for new users.
|
||||
const silly = { minWidth: 240, minHeight: 180, defaultWidth: 100, defaultHeight: 50 };
|
||||
assert.deepEqual(
|
||||
sanitizeWindowBounds(undefined, [PRIMARY], silly),
|
||||
{ width: 240, height: 180, maximized: false },
|
||||
);
|
||||
assert.deepEqual(
|
||||
sanitizeWindowBounds({ x: 0, y: 0, width: 'bad', height: 'bad' }, [PRIMARY], silly),
|
||||
{ width: 240, height: 180, maximized: false },
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user