FIX: Avoid flash-of-unstyled-content in Safari with bug workaround (#25462)

Safari has a bug which means that scripts with the `defer` attribute are executed before stylesheets have finished loading. This is being tracked at https://bugs.webkit.org/show_bug.cgi?id=209261.

This commit works around the problem by introducing a no-op inline `<script>` to the end of our HTML document. This works because defer scripts are guaranteed to run after inline scripts, and inline scripts are guaranteed to run after any preceding stylesheets.

Technically we only need this for Safari. But given that the cost is so low, it makes sense to include it everywhere rather than incurring the complexity of gating it by user-agent.
This commit is contained in:
David Taylor 2024-01-29 17:20:44 +00:00 committed by GitHub
parent df2f63cf74
commit 2457553d0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 0 deletions

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
# Helper to render a no-op inline script tag to work around a safari bug
# which causes `defer` scripts to be run before stylesheets are loaded.
# https://bugs.webkit.org/show_bug.cgi?id=209261
module DeferScriptHelper
def self.safari_workaround_script
<<~HTML.html_safe
<script>#{raw_js}</script>
HTML
end
def self.fingerprint
@fingerprint ||= calculate_fingerprint
end
private
def self.raw_js
"/* Workaround for https://bugs.webkit.org/show_bug.cgi?id=209261 */"
end
def self.calculate_fingerprint
"sha256-#{Digest::SHA256.base64digest(raw_js)}"
end
end

View File

@ -143,5 +143,7 @@
<%- if allow_plugins? %>
<%= build_plugin_html 'server:before-body-close' %>
<%- end %>
<%= DeferScriptHelper.safari_workaround_script %>
</body>
</html>

View File

@ -87,6 +87,7 @@ class ContentSecurityPolicy
end
sources << "'#{SplashScreenHelper.fingerprint}'" if SiteSetting.splash_screen
sources << "'#{DeferScriptHelper.fingerprint}'"
end
end