Skip to content

Commit 3d046b4

Browse files
authored
Inspector: Fix Firefox error reading child window bounds after close (#18389)
> 🤖 *This PR was created by the create-pr skill.* ## Problem In `ChildWindow` (`packages/dev/sharedUiComponents/src/fluent/hoc/childWindow.tsx`), the dispose action that persists the child window's bounds to `localStorage` reads `screenX`/`screenY`/`innerWidth`/`innerHeight` directly off the child `Window`. In Firefox, accessing these properties after the window has closed throws — which is exactly what happens when the user closes the popout (the `unload` handler clears `childWindow` state, the effect re-runs, and the cleanup fires while `childWindow.closed === true`). ## Fix Cache the bounds while the window is still alive and use the cached value at dispose time: - Added a `getBounds()` helper. - Initialize `lastBounds` eagerly when the window opens, so a value is always available even if no later capture happens. - Refresh `lastBounds` in a `beforeunload` listener (fires before the window is destroyed in every close path). - At dispose, refresh once more if the window is still open, then write `lastBounds` to `localStorage`. This eliminates the throw in Firefox while preserving the existing bounds-restore behavior in all browsers. ## Files changed - `packages/dev/sharedUiComponents/src/fluent/hoc/childWindow.tsx`
1 parent 90d5490 commit 3d046b4

1 file changed

Lines changed: 22 additions & 9 deletions

File tree

packages/dev/sharedUiComponents/src/fluent/hoc/childWindow.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ export const ChildWindow: FunctionComponent<PropsWithChildren<ChildWindowProps>>
191191
setWindowState({ mountNode: body, renderer: createDOMRenderer(childWindow.document) });
192192
onOpenChange?.(true);
193193

194+
// Track the most recently observed window bounds. In some browsers (e.g. Firefox), accessing
195+
// properties like screenX on a closed window throws, so we cache the last known good values
196+
// to use as a fallback when the dispose runs after the window has already been closed.
197+
const getBounds = () => ({
198+
left: childWindow.screenX,
199+
top: childWindow.screenY,
200+
width: childWindow.innerWidth,
201+
height: childWindow.innerHeight,
202+
});
203+
let lastBounds = getBounds();
204+
194205
// When the child window is closed for any reason, transition back to a closed state.
195206
const onChildWindowUnload = () => {
196207
setWindowState(undefined);
@@ -200,6 +211,13 @@ export const ChildWindow: FunctionComponent<PropsWithChildren<ChildWindowProps>>
200211
childWindow.addEventListener("unload", onChildWindowUnload, { once: true });
201212
disposeActions.push(() => childWindow.removeEventListener("unload", onChildWindowUnload));
202213

214+
// Capture bounds before the window is unloaded, while its properties are still safe to read.
215+
const onChildWindowBeforeUnload = () => {
216+
lastBounds = getBounds();
217+
};
218+
childWindow.addEventListener("beforeunload", onChildWindowBeforeUnload);
219+
disposeActions.push(() => childWindow.removeEventListener("beforeunload", onChildWindowBeforeUnload));
220+
203221
// If the main window closes, close any open child windows as well (don't leave them orphaned).
204222
const onParentWindowUnload = () => {
205223
childWindow.close();
@@ -213,15 +231,10 @@ export const ChildWindow: FunctionComponent<PropsWithChildren<ChildWindowProps>>
213231
// On dispose, save the window bounds.
214232
disposeActions.push(() => {
215233
if (storageKey) {
216-
localStorage.setItem(
217-
storageKey,
218-
JSON.stringify({
219-
left: childWindow.screenX,
220-
top: childWindow.screenY,
221-
width: childWindow.innerWidth,
222-
height: childWindow.innerHeight,
223-
})
224-
);
234+
if (!childWindow.closed) {
235+
lastBounds = getBounds();
236+
}
237+
localStorage.setItem(storageKey, JSON.stringify(lastBounds));
225238
}
226239
});
227240
}

0 commit comments

Comments
 (0)