In single-process Firefox, when the user switches tabs, this is a synchronous operation. The browser blocks while it loads content into the newly selected tab, then switches to that tab. It indicates that the tab is selected by setting the
selected attribute on the XUL
tab object. Code (including browser code, extensions, or themes) that wants to change the appearance of the selected tab can use the
selected attribute to apply CSS for that tab.
In multiprocess Firefox, tab switching is asynchronous. When the user switches tabs, the chrome process sends a request to the content process to load the page into the newly selected tab. The function in the chrome process then returns immediately, so other code can run. Once the content process is ready, it sends a message back to the chrome process, which then switches tabs in the user interface.
There's also a timer in the chrome process: if the content process has not responded before the timer expires, then the browser switches tabs anyway, and just displays an empty tab containing a spinner, until the content process has finished loading the page. Currently the timer is set to 300 milliseconds.
There are, correspondingly, two attributes used to signal tab selection:
selectedattribute is set synchronously, at the start of the process. It signals that tab selection has started, but at this point the user interface has not yet been updated.
visuallyselectedis now used once the browser has actually updated the user interface, either because the content process is ready or because the timer has expired.
This means that code which wants to style the currently selected tab needs to use the
visuallyselected attribute to do so. If it uses the
selected attribute, then there will be a momentary disconnect in which the newly selected tab's style is updated, but the browser is still displaying the old tab's content.