diff --git a/app/javascript/mastodon/features/emoji/index.ts b/app/javascript/mastodon/features/emoji/index.ts index 4f23dc5395e..541cea9aa99 100644 --- a/app/javascript/mastodon/features/emoji/index.ts +++ b/app/javascript/mastodon/features/emoji/index.ts @@ -1,4 +1,5 @@ import initialState from '@/mastodon/initial_state'; +import { loadWorker } from '@/mastodon/utils/workers'; import { toSupportedLocale } from './locale'; @@ -9,9 +10,8 @@ let worker: Worker | null = null; export async function initializeEmoji() { if (!worker && 'Worker' in window) { try { - worker = new Worker(new URL('./worker', import.meta.url), { + worker = loadWorker(new URL('./worker', import.meta.url), { type: 'module', - credentials: 'omit', }); } catch (err) { console.warn('Error creating web worker:', err); diff --git a/app/javascript/mastodon/features/emoji/loader.ts b/app/javascript/mastodon/features/emoji/loader.ts index 482d9e5c359..454b8383f07 100644 --- a/app/javascript/mastodon/features/emoji/loader.ts +++ b/app/javascript/mastodon/features/emoji/loader.ts @@ -36,15 +36,16 @@ async function fetchAndCheckEtag( ): Promise { const locale = toSupportedLocaleOrCustom(localeOrCustom); - let uri: string; + // Use location.origin as this script may be loaded from a CDN domain. + const url = new URL(location.origin); if (locale === 'custom') { - uri = '/api/v1/custom_emojis'; + url.pathname = '/api/v1/custom_emojis'; } else { - uri = `/packs${isDevelopment() ? '-dev' : ''}/emoji/${locale}.json`; + url.pathname = `/packs${isDevelopment() ? '-dev' : ''}/emoji/${locale}.json`; } const oldEtag = await loadLatestEtag(locale); - const response = await fetch(uri, { + const response = await fetch(url, { headers: { 'Content-Type': 'application/json', 'If-None-Match': oldEtag ?? '', // Send the old ETag to check for modifications diff --git a/app/javascript/mastodon/utils/workers.ts b/app/javascript/mastodon/utils/workers.ts new file mode 100644 index 00000000000..02dd66d86e0 --- /dev/null +++ b/app/javascript/mastodon/utils/workers.ts @@ -0,0 +1,29 @@ +/** + * Loads Web Worker that is compatible with cross-origin scripts for CDNs. + * + * Returns null if the environment doesn't support web workers. + */ +export function loadWorker(url: string | URL, options: WorkerOptions = {}) { + if (!('Worker' in window)) { + return null; + } + + try { + // Check if the script origin and the window origin are the same. + const scriptUrl = new URL(import.meta.url); + if (location.origin === scriptUrl.origin) { + // Not cross-origin, can just load normally. + return new Worker(url, options); + } + } catch (err) { + // In case the URL parsing fails. + console.warn('Error instantiating Worker:', err); + } + + // Import the worker script from a same-origin Blob. + const contents = `import ${JSON.stringify(url)};`; + const blob = URL.createObjectURL( + new Blob([contents], { type: 'text/javascript' }), + ); + return new Worker(blob, options); +} diff --git a/config/environments/development.rb b/config/environments/development.rb index ca9e876e26b..79b491869cc 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -19,6 +19,9 @@ Rails.application.configure do # Enable server timing. config.server_timing = true + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + config.asset_host = ENV['CDN_HOST'] if ENV['CDN_HOST'].present? + # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join('tmp', 'caching-dev.txt').exist? diff --git a/vite.config.mts b/vite.config.mts index 7f93157b7e1..f7871ece4d6 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -65,6 +65,11 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => { // but it needs to be scoped to the whole domain 'Service-Worker-Allowed': '/', }, + hmr: { + // Forcing the protocol to be insecure helps if you are proxying your dev server with SSL, + // because Vite still tries to connect to localhost. + protocol: 'ws', + }, port: 3036, }, build: {