From 662bcade172556cb834cf405e4947681cd5dcd9b Mon Sep 17 00:00:00 2001 From: jquense Date: Mon, 11 Aug 2025 16:00:25 -0400 Subject: [PATCH] fix: crash on invalid cross origin iframe --- src/getWindowEvent.ts | 14 ++++++++++++++ src/useClickOutside.ts | 8 ++------ src/useRootClose.ts | 5 ++--- 3 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 src/getWindowEvent.ts diff --git a/src/getWindowEvent.ts b/src/getWindowEvent.ts new file mode 100644 index 0000000..cc69e88 --- /dev/null +++ b/src/getWindowEvent.ts @@ -0,0 +1,14 @@ +export function getWindowEvent(ownerWindow?: Window | null) { + const win = ownerWindow || window; + + // Store the current event to avoid triggering handlers immediately + // For things rendered in an iframe, the event might originate on the parent window + // so we should fall back to that global event if the local one doesn't exist + // https://github.com/facebook/react/issues/20074 + try { + return win.event ?? win.parent?.event; + } catch (err) { + // catch iframe security errors and fallback to nothing + return undefined; + } +} diff --git a/src/useClickOutside.ts b/src/useClickOutside.ts index ad5d9de..a85e281 100644 --- a/src/useClickOutside.ts +++ b/src/useClickOutside.ts @@ -5,6 +5,7 @@ import { useCallback, useEffect, useRef } from 'react'; import useEventCallback from '@restart/hooks/useEventCallback'; import warning from 'warning'; +import { getWindowEvent } from './getWindowEvent.js'; const noop = () => {}; @@ -100,13 +101,8 @@ function useClickOutside( if (disabled || ref == null) return undefined; const doc = ownerDocument(getRefTarget(ref)!); - const ownerWindow = doc.defaultView || window; - // Store the current event to avoid triggering handlers immediately - // For things rendered in an iframe, the event might originate on the parent window - // so we should fall back to that global event if the local one doesn't exist - // https://github.com/facebook/react/issues/20074 - let currentEvent = ownerWindow.event ?? ownerWindow.parent?.event; + let currentEvent = getWindowEvent(doc.defaultView); let removeInitialTriggerListener: (() => void) | null = null; if (InitialTriggerEvents[clickTrigger]) { diff --git a/src/useRootClose.ts b/src/useRootClose.ts index 991f21f..b18d2ee 100644 --- a/src/useRootClose.ts +++ b/src/useRootClose.ts @@ -8,6 +8,7 @@ import useClickOutside, { getRefTarget, } from './useClickOutside.js'; import { isEscKey } from './utils.js'; +import { getWindowEvent } from './getWindowEvent.js'; const noop = () => {}; @@ -47,9 +48,7 @@ function useRootClose( const doc = ownerDocument(getRefTarget(ref)!); - // Store the current event to avoid triggering handlers immediately - // https://github.com/facebook/react/issues/20074 - let currentEvent = (doc.defaultView || window).event; + let currentEvent = getWindowEvent(doc.defaultView); const removeKeyupListener = listen(doc as any, 'keyup', (e) => { // skip if this event is the same as the one running when we added the handlers