rafaelmardojai / firefox-gnome-theme

A GNOME👣 theme for Firefox🔥
The Unlicense
3.4k stars 155 forks source link

Performance regression related to gnomeTheme.hideSingleTab #751

Closed piousdeer closed 6 months ago

piousdeer commented 7 months ago

Describe the bug

This tabsbar.css rule introduced in #747 creates lag that blocks the whole browser (for 800+ms in some scenarios) when Firefox UI renders a lot of stuff:

/* OPTIONAL: Hide single tab */
@media (-moz-bool-pref: "gnomeTheme.hideSingleTab") {
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery {
        visibility: collapse;
    }
}

To Reproduce

  1. In Firefox settings, enable "Ctrl+Tab cycles through tabs in recently used order"
  2. In about:config, enable gnomeTheme.hideSingleTab
  3. Open a new window and try switching between a few tabs via Ctrl+Tab
  4. Hold Ctrl+T until you have 500+ tabs
  5. Ctrl+Tab and observe the lag

Expected behavior

No lag

Screenshots

Setup information

Additional context

In case it helps someone debug this type of issue in the future: I used the Shift+F5 profiler with the Firefox preset and saw spikes in DOM activity correlating to the lag. The call stack mentioned XUL and selectors, so the issue was isolated to Firefox CSS. I then used the Style Editor tab in the Browser Toolbox to quickly bisect through imports in userChrome.css (note that you have to restart Firefox to bring an import back after erasing it), that's how I isolated it to tabsbar.css.

piousdeer commented 7 months ago

I haven't dove into DOM to look for simpler selectors, but simply consolidating those using :is fixes the lag while keeping the intended behavior.

@media (-moz-bool-pref: "gnomeTheme.hideSingleTab") {
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) :is(
        tab,
        tab ~ toolbarbutton,
        tab ~ #tabbrowser-arrowscrollbox-periphery
    ) {
        visibility: collapse;
    }
}
piousdeer commented 7 months ago

In fact, I've noticed my context menus open quicker with this tweak. To see if it's placebo, I exaggerated the problematic rule into

@media (-moz-bool-pref: "gnomeTheme.hideSingleTab") {
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ toolbarbutton,
    #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) tab ~ #tabbrowser-arrowscrollbox-periphery{
        visibility: collapse;
    }
}

and indeed, opening a context menu or doing pretty much anything slowed everything down to a crawl. So, this is not limited to Ctrl+Tab.

rafaelmardojai commented 7 months ago

I haven't dove into DOM to look for simpler selectors, but simply consolidating those using :is fixes the lag while keeping the intended behavior.

@media (-moz-bool-pref: "gnomeTheme.hideSingleTab") {
  #tabbrowser-tabs:not(:has(tab:not([hidden="true"]) ~ tab:not([hidden="true"]))) :is(
      tab,
      tab ~ toolbarbutton,
      tab ~ #tabbrowser-arrowscrollbox-periphery
  ) {
      visibility: collapse;
  }
}

Thanks for the detailed examination, are you willing to send a patch with these changes?

piousdeer commented 7 months ago

Sure, a bit later today