diff --git a/packages/apputils/src/windowresolver.ts b/packages/apputils/src/windowresolver.ts index 4bad4b7f8e59..c1d981e7fa3d 100644 --- a/packages/apputils/src/windowresolver.ts +++ b/packages/apputils/src/windowresolver.ts @@ -84,6 +84,18 @@ namespace Private { */ const WINDOW = `${PREFIX}:window`; + /** + * Current beacon request + * + * #### Notes + * We keep track of the current request so that we can ignore our own beacon + * requests. This is to work around a bug in Safari, where Safari sometimes + * triggers local storage events for changes made by the current tab. See + * https://github.com/jupyterlab/jupyterlab/issues/6921#issuecomment-540817283 + * for more details. + */ + let currentBeaconRequest: string | null = null; + /** * A potential preferred default window name. */ @@ -123,7 +135,11 @@ namespace Private { } // If the beacon was fired, respond with a ping. - if (key === BEACON && candidate !== null) { + if ( + key === BEACON && + newValue !== currentBeaconRequest && + candidate !== null + ) { ping(resolved ? name : candidate); return; } @@ -163,6 +179,7 @@ namespace Private { */ function reject(): void { resolved = true; + currentBeaconRequest = null; delegate.reject(`Window name candidate "${candidate}" already exists`); } @@ -197,12 +214,14 @@ namespace Private { } resolved = true; + currentBeaconRequest = null; delegate.resolve((name = candidate)); ping(name); }, TIMEOUT); // Fire the beacon to collect other windows' names. - localStorage.setItem(BEACON, `${Math.random()}-${new Date().getTime()}`); + currentBeaconRequest = `${Math.random()}-${new Date().getTime()}`; + localStorage.setItem(BEACON, currentBeaconRequest); return delegate.promise; }