mirror of
https://github.com/glitch-soc/mastodon.git
synced 2026-03-29 03:00:33 +02:00
Add ability to include inline javascript (#37459)
This commit is contained in:
@@ -1,6 +1,22 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ThemeHelper
|
module ThemeHelper
|
||||||
|
def javascript_inline_tag(path)
|
||||||
|
entry = InlineScriptManager.instance.file(path)
|
||||||
|
|
||||||
|
# Only add hash if we don't allow arbitrary includes already, otherwise it's going
|
||||||
|
# to break the React Tools browser extension or other inline scripts
|
||||||
|
unless Rails.env.development? && request.content_security_policy.dup.script_src.include?("'unsafe-inline'")
|
||||||
|
request.content_security_policy = request.content_security_policy.clone.tap do |policy|
|
||||||
|
values = policy.script_src
|
||||||
|
values << "'sha256-#{entry[:digest]}'"
|
||||||
|
policy.script_src(*values)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
content_tag(:script, entry[:contents], type: 'text/javascript')
|
||||||
|
end
|
||||||
|
|
||||||
def theme_style_tags(theme)
|
def theme_style_tags(theme)
|
||||||
if theme == 'system'
|
if theme == 'system'
|
||||||
''.html_safe.tap do |tags|
|
''.html_safe.tap do |tags|
|
||||||
|
|||||||
23
app/javascript/inline/theme-selection.js
Normal file
23
app/javascript/inline/theme-selection.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
(function (element) {
|
||||||
|
const {userTheme} = element.dataset;
|
||||||
|
|
||||||
|
const colorSchemeMediaWatcher = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
const contrastMediaWatcher = window.matchMedia('(prefers-contrast: more)');
|
||||||
|
|
||||||
|
const updateColorScheme = () => {
|
||||||
|
const useDarkMode = userTheme === 'system' ? colorSchemeMediaWatcher.matches : userTheme !== 'mastodon-light';
|
||||||
|
element.dataset.mode = useDarkMode ? 'dark' : 'light';
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateContrast = () => {
|
||||||
|
const useHighContrast = userTheme === 'contrast' || contrastMediaWatcher.matches;
|
||||||
|
|
||||||
|
element.dataset.contrast = useHighContrast ? 'high' : 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
colorSchemeMediaWatcher.addEventListener('change', updateColorScheme);
|
||||||
|
contrastMediaWatcher.addEventListener('change', updateContrast);
|
||||||
|
|
||||||
|
updateColorScheme();
|
||||||
|
updateContrast();
|
||||||
|
})(document.documentElement);
|
||||||
31
app/lib/inline_script_manager.rb
Normal file
31
app/lib/inline_script_manager.rb
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'singleton'
|
||||||
|
|
||||||
|
class InlineScriptManager
|
||||||
|
include Singleton
|
||||||
|
include ActionView::Helpers::TagHelper
|
||||||
|
include ActionView::Helpers::JavaScriptHelper
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@cached_files = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def file(name)
|
||||||
|
@cached_files[name] ||= load_file(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_file(name)
|
||||||
|
path = Pathname.new(name).cleanpath
|
||||||
|
raise ArgumentError, "Invalid inline javascript path: #{path}" if path.to_s.start_with?('..')
|
||||||
|
|
||||||
|
path = Rails.root.join('app', 'javascript', 'inline', path)
|
||||||
|
|
||||||
|
contents = javascript_cdata_section(path.read)
|
||||||
|
digest = Digest::SHA256.base64digest(contents)
|
||||||
|
|
||||||
|
{ contents:, digest: }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
- if use_mask_icon?
|
- if use_mask_icon?
|
||||||
%link{ rel: 'mask-icon', href: frontend_asset_path('images/logo-symbol-icon.svg'), color: '#6364FF' }/
|
%link{ rel: 'mask-icon', href: frontend_asset_path('images/logo-symbol-icon.svg'), color: '#6364FF' }/
|
||||||
%link{ rel: 'manifest', href: manifest_path(format: :json) }/
|
%link{ rel: 'manifest', href: manifest_path(format: :json) }/
|
||||||
|
= javascript_inline_tag 'theme-selection.js'
|
||||||
= theme_color_tags current_theme
|
= theme_color_tags current_theme
|
||||||
%meta{ name: 'mobile-web-app-capable', content: 'yes' }/
|
%meta{ name: 'mobile-web-app-capable', content: 'yes' }/
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ RSpec.describe 'Content-Security-Policy' do
|
|||||||
img-src 'self' data: blob: #{local_domain}
|
img-src 'self' data: blob: #{local_domain}
|
||||||
manifest-src 'self' #{local_domain}
|
manifest-src 'self' #{local_domain}
|
||||||
media-src 'self' data: #{local_domain}
|
media-src 'self' data: #{local_domain}
|
||||||
script-src 'self' #{local_domain} 'wasm-unsafe-eval'
|
script-src 'self' #{local_domain} 'wasm-unsafe-eval' 'sha256-Q/2Cjx8v06hAdOF8/DeBUpsmBcSj7sLN3I/WpTF8T8c='
|
||||||
style-src 'self' #{local_domain} 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='
|
style-src 'self' #{local_domain} 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='
|
||||||
worker-src 'self' blob: #{local_domain}
|
worker-src 'self' blob: #{local_domain}
|
||||||
CSP
|
CSP
|
||||||
|
|||||||
Reference in New Issue
Block a user