Page MenuHomePhabricator

Flash of unstyled content (FOUC) on Login page and Preferences (and possibly other highly visible pages?)
Closed, ResolvedPublic

Description

I've been observing FOUCs on Special:UserLogin and Special:Preferences page since the ResourceLoader load order changes a few months ago. Several other users have also mentioned that they've also been observing this. On Special:Preferences, the behavior is especially ugly after form submission. The old success box is shown for a second and then the whole page changes and the mw.notification style message is shown later. This is really sad for a highly visible page like the login/signup form.

Event Timeline

Glaisher raised the priority of this task from to Medium.
Glaisher updated the task description. (Show Details)
Glaisher subscribed.
Catrope set Security to None.
Catrope added a subscriber: Krinkle.

Confirmed here, also on my private test wikis running git master and not much else. Almost always on Special:UserLogin. Also frequently seen on WMF wikis.

saper renamed this task from FOUCs on Login page and Preferences (and possibly other highly visible pages?) to FOUC on Login page and Preferences (and possibly other highly visible pages?).Oct 20 2015, 12:56 PM

Putting

$ret .= $this->buildCssLinks() . "\n";

before

$ret .= $this->getInlineHeadScripts() . "\n";

in includes/OutputPage.php ca. line 2713 fixes the login page issue for me (not a proper solution, though)

Change 247624 had a related patch set uploaded (by saper):
Let CSS load before top JS queue starts

https://gerrit.wikimedia.org/r/247624

The preferences problem appears in wmf/1.26wmf17 and is not there with wmf/1.26wmf16, which suggests T107399 .

I see no way to fix this in the preferences, since preferences.js is already loaded as the "top" script, which should be executed early.

This is triggered by T107399 most likely indeed, but not "caused" in so far that the dependent styles for the basic layout should not be loaded with JavaScript in the first place.

Shuffling around the items in the DOM won't solve this. It may hide the bug when using a fast connection by disproportionally prioritising this particular script, but it's not a solution. (And will regress other things.)

The solution will most likely need to come from pinpointing why the layout is influenced by JavaScript, and making it render similarly without JavaScript (with JavaScript only expanding the layout, not necessarily changing the layout and positions of elements already there).

Also make sure the base styles for the view without JavaScript (yet) are loaded with addModuleStyles.

It should not be needed to load styles before the top queue starts, there is no FOUC for other things either (e.g. history page, article view, skin framework etc.). The order was carefully chosen and is unlikely to change because of this bug only.

Frankly I don't know why UserLogin is somehow special. Maybe additional top scripts weigh more.

But with 1.26wmf16 everything is much smoother. I also tried quickly reloading other pages and yes, you get a quick flash every 15 times or so - not a big problem but I think this indicates this is a general issue.
For UserLogin it is almost always, and very visible. Preferences are disaster imho.

As far as Preferences go, their top-loading script creates a whole ul#preftoc part (adds <ul> and <li>) for preferences tabs.
I think ages ago it was done in HTML, but got "modernized".

I was porting two new skins recently and I was scratching my head what is wrong.

I don't know why "top queue" gets delayed considerably, but effectively "top queue" gets reduced to a "slightly faster bottom queue", that's not good :(

I have tried commenting out all addModuleStyles and addModule out of the UserLogin page and I still keep getting this. Additional JS is only loaded in case of signup and it does not seem to smash DOM significantly.

One thing that Preferences and UserLogin have in common is a call to OutputPage::disallowUserJs()

`
includes/specials/SpecialChangeEmail.php:		$out->disallowUserJs();
includes/specials/SpecialChangePassword.php:		$this->getOutput()->disallowUserJs();
includes/specials/SpecialJavaScriptTest.php:		$out->disallowUserJs();
includes/specials/SpecialPreferences.php:		$out->disallowUserJs(); # Prevent hijacked user scripts from sniffing passwords etc.
includes/specials/SpecialResetTokens.php:		$this->getOutput()->disallowUserJs();
includes/specials/SpecialUserlogin.php:		$out->disallowUserJs(); // just in case...
`

I have tried commenting out all addModuleStyles and addModule out of the UserLogin page and I still keep getting this. Additional JS is only loaded in case of signup and it does not seem to smash DOM significantly.

Try only commenting out addModule calls (keep addModuleStyles, since only the script queue has top/bottom separation) and work towards the resulting page rendering correctly with only the HTML response and style sheets.

This most likely means some of the styles will need to be extracted (if not already) into a styles-only module. And since interactive tabs require JavaScript, we do need some refactoring to make the no-js mode not broken (and kept as continuous page, like it is now). But the layout needs to be brought closer together.

Note you can also use .client-js and .client-nojs classes in CSS to differentiate with the target rendering. That way, the layout can have the tabs ready from the first render, without confusing the nojs mode.

What I wanted to say is that even the most conservative approach (no special page specific RL *modules at all*) triggers this bug.

I opened https://gerrit.wikimedia.org/r/#/c/247728 to refactor RL modules and start improving things.

Shall I open a separate bug for "Generate Special:Preferences tabs via HTML" to see if there is a general agreement to do so?

While I realize it may be annoying to obtain, is there a screenshot or frame capture for this issue on Special:UserLogin?

While I realize it may be annoying to obtain, is there a screenshot or frame capture for this issue on Special:UserLogin?

LoginPage FOUC.png (1×1 px, 111 KB)

MZMcBride raised the priority of this task from Medium to High.Oct 21 2015, 4:21 PM

Ouch!

MZMcBride renamed this task from FOUC on Login page and Preferences (and possibly other highly visible pages?) to Flash of unstyled content (FOUC) on Login page and Preferences (and possibly other highly visible pages?).Oct 21 2015, 4:21 PM

Another interesting effect which results from the delayed JavaScript execution: T112379

Change 250287 had a related patch set uploaded (by TheDJ):
Rework the Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/250287

Change 250288 had a related patch set uploaded (by TheDJ):
Rework Vector style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/250288

This issue has been independently confirmed by multiple people, so I am sure that it is real, but I have not been able to reproduce it.

If the bug only appears when network latency is sufficiently high, it might be possible to make it manifest reliably by inserting sleep() calls into the code.

Edit: I am able to reproduce this with the preferences page, but not the login page.

@ori Can you try getting enough latency by using Tor? I get it with Tor almost every time.

If you need latency and have a Mac, try using the Network Link Conditioner (NSHipster blog, Apple Download)

Change 251815 had a related patch set uploaded (by TheDJ):
Rework Monobook style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251815

Change 250287 merged by jenkins-bot:
Rework the Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/250287

Change 250288 merged by jenkins-bot:
Rework Vector style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/250288

Change 251815 merged by jenkins-bot:
Rework Monobook style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251815

Change 251839 had a related patch set uploaded (by Ori.livneh):
Rework the Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251839

Change 251840 had a related patch set uploaded (by Ori.livneh):
Rework Vector style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251840

Change 251841 had a related patch set uploaded (by Ori.livneh):
Rework Monobook style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251841

Change 251839 merged by jenkins-bot:
Rework the Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251839

Change 251840 merged by jenkins-bot:
Rework Vector style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251840

Change 251841 merged by jenkins-bot:
Rework Monobook style of Preferences to prevent FOUC

https://gerrit.wikimedia.org/r/251841

So preferences tackled.. Do we still have an issue on the login page ?

Change 251848 had a related patch set uploaded (by TheDJ):
Fix regression in I24d9b16

https://gerrit.wikimedia.org/r/251848

So preferences tackled.. Do we still have an issue on the login page ?

Thanks for working on this!
Looks like the "mw-preferences-messagebox successbox" doesn't get hidden anymore. Is that intentional? I can still observe it on the login page.

@Glaisher, yeah krinkle noticed that to, the "Fix regression in I24d9b16" patch will deal with that.

Change 251848 merged by jenkins-bot:
Fix regression in I24d9b16

https://gerrit.wikimedia.org/r/251848

Regarding the Login page. I personally only experience this with Firefox. From what I can tell from the profile, it seems that the initial inline script tags trigger the first paint, and the parsing of the first stylesheet being done, triggers the second paint.

A diff of a random other page vs. login page.

Found it.

'mediawiki.special' => array(
                'position' => 'top',
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.css',
        ),

+ Vector's skinStyles override of this. Actually, come to think of it, that was half of what was wrong with the styling of preferences as well...

This module is broken, but fixing it doesn't fix the login page on Firefox either.

edited because confusing

:( Or not.

I can still reproduce, even after fixing obvious things like that. And the weird thing is, I reworked the output to be almost equal to whatever is in any other page... The only thing i can imagine is that the startupmodule is at fault (different JS variables ?), or that it is a FF behavior, where sometimes the page is just layout'ed and painted before the first CSS stylesheet has finished downloading/parsing in a race condition.

This goes beyond my skill level now honestly.

I excluded: All the login specific styles, the mediawiki.ui styles, mediawiki.special styles. The Site modules (added or removed), skins (happens with monobook and vector). Even meta robots.

@TheDJ it is fixable by reverting T107399... Maybe OutputPage::disallowUserJs() makes a difference? It is only used on few pages that cause the flash.

TheDJ changed the task status from Open to Stalled.Nov 19 2015, 10:46 PM
TheDJ removed a project: Patch-For-Review.

What would be the best strategy to pre-select active tab based on the # on the preferences screen without causing the page to reflash (HTML, CSS and then pre-selecting code run in async)?

There is no easy answer. If the tabs are constructed in JavaScript, care should be taken to detect the hash during that construction so that the first appearance of the tabs is "correct" and there is no flash of the wrong tab. Using JavaScript however has the downside that instead of flashing the wrong tab, it would flash the whole page wrong (as was the case until recently).

Now that the tabs are built server-side, we can't initialise the tabs the same way since the hash fragment is unavailable to the server-side.

Two solutions come to mind:

  • Use the query string instead of hash fragment. Instead of using a hash fragment, we could use a query string instead. This way the server-side can preselect the appropiate tab automatically. In JavaScript, just like now, we could hijack the clicks and switch tabs accordingly. The browser address bar can be kept in sync using the History API.

Thanks @Krinkle. You are right we are talking here about server-generated HTML code and only the final touch (current selection) needs to be brought into play.

I am not thinking only about preferences here, there might be other areas were we might need just that. And now I've heard server-side generation is cool again.

Query string suggestion is actually very good. We could even use URL path for that. Just like in the good old 1997 :)

I'll try to have a look at the preferences code again.

Change 247624 abandoned by Krinkle:
Let CSS load before top JS queue starts

Reason:
Closing for now. Discussion continues on Phabricator.

https://gerrit.wikimedia.org/r/247624

Any updates on this? Are T142129 and T109837 supposed to fix this?

No. My previous recommendation still stands. We must not change the layout of the page client-side at page load. Rather it should be done server-side for the initial page load.

Just need to report that a fresh install of 1.29.1 still has this problem :(

Taking a quick peek at this per an inquiry from @saper. Specifically looking at the login page...

I used to have the FOUC on login page constantly, but I'm unable to reproduce it on my laptop or desktop MW-Vagrant setups now with current master, even when throttling Firefox's network speed in the debug panel or adding sleep(10); to load.php. (Testing Firefox 61.0.2 on Linux and Mac.) Not sure yet if it's "fixed" or if I haven't found the right way to repro.

On Special:UserLogin, the HTML bits break down roughly:

doctype, charset, and title ...
<script>... replace client-nojs with client-js ...</script>
<script>... push variables and tokens and additional modules on the RL queue ...</script>
<link rel="stylesheet" href="... RL styles ..."/>
<script async="" src="... RL startup ..."></script>
... more metadata and body ...
<script>... warn about deprecated modules and record the server execution time ...</script>
end.

The synchronous <script>s are very simple, adjusting a class and appending stuff to a queue, and are all inline. The styles are linked before the RL startup code is loaded, but that's via a <script async> so shouldn't trigger rendering due to waiting (and if it did, you wouldn't see any text because that hasn't been parsed yet).

In my testing adding artificial delays on the styles but not on the scripts, I still don't see a FOUC -- just a blank page until it shows the login screen with styles.

@saper do you still see this on current master, and can you add some repro details if so? I really want this fixed up if it's still affecting people, as it's maddening. :)

Two years without updates later: Can anyone still reproduce this problem in 1.35 or current master?
If yes, then please provide clear steps to reproduce.
Otherwise this task will get closed (as it shouldn't become a catch-all for any FOUC issues).
Thanks.

Two years without updates later: Can anyone still reproduce this problem in 1.35 or current master?
If yes, then please provide clear steps to reproduce.
Otherwise this task will get closed (as it shouldn't become a catch-all for any FOUC issues).

No followup comments.
Assuming this is resolved nowadays (last comment successfully reproducing the problem was added 3 years ago).
If someone can still reproduce this problem, then please follow https://www.mediawiki.org/wiki/How_to_report_a_bug and create a new ticket.
Thanks a lot!