reversed(top()) code tags rss about

The fucking Firefox lag

May 21, 2023
[issue]

Some time ago Firefox started to occasionally hang for maybe 20 seconds during which nothing was drawn on the window. Later I’ve noticed that it happened every time a context menu was about to be shown, same for some dialogs, all drop-downs and sometimes on opening of a new tab. I think it started after an update and naturally expected it to go away after another one. Sound also started to have issues at some point, but it wasn’t really a problem. The version in use is an ESR (102), but regressions happen in ESRs as well. Plus it didn’t work in a tiling WM, which also tend to manifest weird behaviours of applications. Yesterday, I’ve finally fixed it.

So why is it “fucking”? Keep reading…

The setup

I have two X.Org servers running in parallel with each one using a separate user account. This allows having two largely independent environments that don’t clash most of the time and can be easily switched. Works as I would want it except that inactive server can block some of its apps.

The issue didn’t occur in the main user account with euclid-wm, but only in a second one with awesome. Which is a bit weird given that the first WM isn’t reparenting and that’s one of the things that can cause trouble with windows.

Since the issue started to happen I had to stop using context menu, which isn’t a big loss. Drop-down menus was a bigger trouble, but I only needed to work around it once. All in all, it was bearable, although I was still annoyed on accidentally hitting right mouse button from time to time.

Looking for the cause

Surprisingly, looking for Firefox issues in relation to awesome didn’t yield any results on multiple search attempts.

Adding all WM workarounds I know of:

wmname LG3D -v 2>/dev/null
export NO_AT_BRIDGE=1
export DE=gnome
export DESKTOP_SESSION=gnome
export SWT_GTK3=0
export _JAVA_AWT_WM_NONREPARENTING=1

had no effect.

Dropping configuration of awesome didn’t help.

Trying to use a separate Firefox profile didn’t help either, instead it demonstrated the same hang in --ProfileManager dialog.

After eventually getting sick with this behaviour I also tried using Firefox on a third account with awesome but with essentially no configuration at all in case something in .xinitrc, .xresources, etc. could cause this. This time it worked!

30 seconds of bisecting configuration files copied from the “bad” account and the offending line was determined to be:

export PULSE_SERVER=tcp:127.0.0.1:5715

Remember sound issues mentioned above?

Explanation

Another part of the setup, which I would have never thought of connecting to the issue with context menus, was sharing one pipewire instance among two users. This was done just in case I’ll ever need audio there, which was almost never necessary.

As you might know, both pulseaudio and pipewire run per each user session, rather than per system. No idea why, maybe it can be worked around, but this is something to deal with. One of the side-effects of this is having multiple instances for singular things like an audio card or Bluetooth headphones. Bluetooth headphones in particular can’t be shared between my two users. They first connect to pipewire-media-session instance of the main user and using the same device under another user won’t work (instances start to fight over the device). Even if it did, sounds from different users probably wouldn’t mix. So it basically doesn’t work the way I’d want because device gets bound to one of the users rather than to a common system.

Solution

Initially I had this in ~/.config/pipewire/pipewire-pulse.conf:

context.modules = [
    { name = libpipewire-module-protocol-pulse
        args = {
            server.address = [
                "unix:native"
                "tcp:127.0.0.1:5715"                # IPv4 on a single address
            ]
            # ...
        }
    }
    # ...
]

As it turns out, this implies client.access = "restricted" (whatever “restricted” means, docs aren’t terribly clear on that). Fixed version:

context.modules = [
    { name = libpipewire-module-protocol-pulse
        args = {
            server.address = [
                "unix:native"
                {  address = "tcp:127.0.0.1:5715"   # IPv4 on a single address
                   client.access = "unrestricted"   # permissions for clients
                }
            ]
            # ...
        }
    }
    # ...
]

Why not accept connections by default? Or not drop the “restricted” ones? Or connect but return a failure?

Anyway, once pipewire started accepting connections, menus and dialogs in Firefox started to work… What could be more obvious?

Last words

Just to clarify, the lag is “fucking” because fucking developers of the fucking Firefox somehow managed to tie displaying of menus and pop-ups to sounds despite the fact that there seem to be no UI sounds in the fucking Firefox! How do you even do that?

Why do I even use Firefox? Because the Web is literally broken and using something other than Firefox or a Chrome derivative causes even more trouble.