Skip to content

Fix/beforeunload recording disconnect#17052

Open
raghuramala54 wants to merge 2 commits intojitsi:masterfrom
raghuramala54:fix/beforeunload-recording-disconnect
Open

Fix/beforeunload recording disconnect#17052
raghuramala54 wants to merge 2 commits intojitsi:masterfrom
raghuramala54:fix/beforeunload-recording-disconnect

Conversation

@raghuramala54
Copy link

Problem

When pressing the browser's X button during a meeting with an active
recording, two things happened incorrectly even if the user clicked Cancel
on the browser's confirmation dialog:

  1. The user was disconnected from the meeting
  2. The local recording was stopped

Both happened because conferenceWillLeave and stopLocalVideoRecording
were dispatched synchronously inside beforeUnloadHandler — before the
user even saw the confirmation popup. Since JavaScript runs the entire
handler to completion before the browser shows the popup, both actions
happened regardless of the user's choice.

Root Cause

The beforeunload event fires and runs the entire handler synchronously.
e.preventDefault() only sets a flag telling the browser to show a popup
after the handler finishes — it does not pause execution. So by the time
the user sees the popup, the disconnect and recording stop had already
happened.

Fix

  • beforeUnloadHandler now only shows the confirmation popup when a
    recording is active. It dispatches nothing.
  • Added a separate unloadHandler which fires exclusively when the user
    confirms leaving. This is where stopLocalVideoRecording and
    conferenceWillLeave are dispatched.
  • When disableBeforeUnloadHandlers is true (no popup possible), both
    actions are still handled inside beforeUnloadHandler since cancel is
    not possible in that case.
  • Updated _removeUnloadHandler to clean up both handlers properly to
    prevent memory leaks.

Testing

  1. Join a meeting and start a local recording
  2. Press the browser X button
  3. Click Cancel on the popup
  4. Still in the meeting
  5. Recording still running
  6. Repeat steps 1-2, click Leave
  7. Recording stops correctly
  8. Disconnected from meeting correctly

Fixes #16972

raghuramala54 and others added 2 commits February 28, 2026 14:58
…dialog

When pressing the browser X button during a meeting, conferenceWillLeave
was being dispatched immediately inside the beforeunload handler, causing
the user to be disconnected even if they clicked Cancel on the browser's
confirmation dialog.

Move conferenceWillLeave to a separate unload handler which only fires
when the user actually confirms leaving the page.

Fixes jitsi#16972
@jitsi-jenkins
Copy link

Hi, thanks for your contribution!
If you haven't already done so, could you please make sure you sign our CLA (https://jitsi.org/icla for individuals and https://jitsi.org/ccla for corporations)? We would unfortunately be unable to merge your patch unless we have that piece :(.

@damencho
Copy link
Member

damencho commented Mar 1, 2026

Have you tested this? Make sure there are two participants in the meeting when testing and after clicking cancel there are two participants that stay in the meeting.

You can see in the logs.

2026-03-01T14:57:21.652Z [INFO] [xmpp:Xmpp] <Ll.disconnect>:  XMPP disconnect triggered by the event=beforeunload
08:57:21.652 

@Alok-work23
Copy link

Hey @raghuramala54 @damencho, I was looking into why the XMPP disconnect is still being triggered by event = beforeunload.
I think it was due to react/features/visitors/middleware.ts has its own beforeUnloadHandler that calls WebsocketClient.getInstance().disconnect() directly on beforeunload.
Applying the same beforeunloadunload separation pattern to visitors/middleware.ts should fix the remaining disconnect issue.
I am not sure but may be it will help.

@damencho
Copy link
Member

damencho commented Mar 1, 2026

Find the where the dosconnect message is printed add a breakpoint there and you will figure this out. It is not the visitors.

@Alok-work23
Copy link

Screenshot (720) I added a breakpoint where the disconnect message is printed in `lib-jitsi-meet/modules/xmpp/xmpp.ts`.

Function at xmpp.ts:386 is the handleDisconnect listener registered in lib-jitsi-meet itself:

const events = `${this.options.disableBeforeUnloadHandlers ? '' : 'beforeunload '}unload`;

const handleDisconnect = ev => {
    this.disconnect(ev); // ← this fires on beforeunload
};

for (const event of events.split(' ')) {
    window.addEventListener(event, handleDisconnect);
}

So the XMPP disconnect is coming entirely from lib-jitsi-meet, not from jitsi-meet. The fix in this PR (moving dispatches to unloadHandler) is correct for the jitsi-meet side, but lib-jitsi-meet also needs a fix to not register handleDisconnect on beforeunload.

Should the fix be made in lib-jitsi-meet to only listen to unload and not beforeunload?

@damencho
Copy link
Member

damencho commented Mar 3, 2026

Should the fix be made in lib-jitsi-meet to only listen to unload and not beforeunload?

Not sure what is the logic there and why it was added, it depends on that.

@Alok-work23
Copy link

Ok

@Alok-work23
Copy link

Hii @damencho ,
I found the related issue jitsi/lib-jitsi-meet#1384 in lib-jitsi-meet from 2020 which describes the exact same problem. In that issue you commented like:

'So we try to desperately send the last unavailable stanza to prevent ghost participants, so removing that is not a good idea.'

After going through the issue comments what I think of the fix needs to be more nuanced:

  • On beforeunload → send the unavailable stanza only (do not disconnect fully)
  • On unload (user confirmed leaving) → fully disconnect
  • If user cancels → re-send an available presence stanza to restore the participant in the meeting

What do you think on it ?

@damencho
Copy link
Member

damencho commented Mar 3, 2026

  • On beforeunload → send the unavailable stanza only (do not disconnect fully)

That is the disconnect - sending an unavailable stanza.

  • If user cancels → re-send an available presence stanza to restore the participant in the meeting

This is a rejoin, like reloading the page, you disconnect clear the whole UI and join again.

@damencho
Copy link
Member

damencho commented Mar 3, 2026

Nice job on the git and github archeology.

@raghuramala54
Copy link
Author

Hi @damencho i just tried debugging it and noticed the xmpp disconnect is getting fired even before the pop shows up.

and when i checked xmpp.ts file it shows the disconnect is called on beforeunload and unload both. I will change it in lib-jitsi-meet first i guess.

@Alok-work23
Copy link

Hi @raghuramala54 and @damencho,

I applied the changes from this PR locally and tried to implement the missing fix in lib-jitsi-meet.

lib-jitsi-meet/modules/xmpp/xmpp.ts registers handleDisconnect on beforeunload, which causes XMPP to disconnect before the user even sees the popup. So, I removed the beforeunload and only registers it on unload.

The lib-jitsi-meet fix (modules/xmpp/xmpp.ts):

// Only disconnect on unload (user confirmed leaving).
// mod_smacks (XEP-0198) handles ghost participants server-side.
window.addEventListener('unload', handleDisconnect);

// Show confirmation popup on beforeunload only — do NOT disconnect.
if (!this.options.disableBeforeUnloadHandlers) {
    window.addEventListener('beforeunload', ev => {
        ev.preventDefault();
        ev.returnValue = '';
    });
}

As far as concern about ghost participants, I found mod_smacks (XEP-0198) is already enabled and configured.
Prosody automatically removes participants after the hibernation period if the connection drops, so the client does not need to send an unavailable stanza on beforeunload.

I tested both fixes together — the changes from this PR and the lib-jitsi-meet fix. These two changes together make it work correctly.

@raghuramala54 I saw you mentioned you'll change it in lib-jitsi-meet — I've already implemented and tested it. I'm happy to open the PR in lib-jitsi-meet if that helps, or you can take it forward. Whatever works best for you.

@damencho
Copy link
Member

damencho commented Mar 9, 2026

As far as concern about ghost participants, I found mod_smacks (XEP-0198) is already enabled and configured.
Prosody automatically removes participants after the hibernation period if the connection drops, so the client does not need to send an unavailable stanza on beforeunload.

That is correct. But today when I leave by closing the tab, I send successfully a presence unavailable and my thumbnail disappears. With these changes and as I read about what can be done during unload and breforeunload my thumbnail will always be there for 60 seconds, which is not desired and is change of behavior we cannot accept.

@Alok-work23
Copy link

Alok-work23 commented Mar 10, 2026

Hi @damencho,
I tested again with two windows. When I close the tab of Window 1, the participant thumbnail disappears instantly in Window 2 , there is no 60 second delay. I will look more about this concern again and update it.

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.

Press the x in your browser during a recording but force leave the meeting, why ?

4 participants