Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ See docs/process.md for more on how version tagging works.
-----------------------
- `-sUSE_WEBGPU` was removed in favor of the external port Emdawnwebgpu which
are used via `--use-port=emdawnwebgpu`. See 4.0.10 release notes for details.
- A new `CROSS_ORIGIN` setting was added in order to work around issues hosting
emscripten programs across different origins (#25581)

4.0.17 - 10/17/25
-----------------
Expand Down
12 changes: 12 additions & 0 deletions site/source/docs/tools_reference/settings_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3341,3 +3341,15 @@ It's currently only available under a flag in certain browsers,
so we disable it by default to save on code size.

Default value: false

.. _cross_origin:

CROSS_ORIGIN
============

If the emscripten-generated program is hosted on separate origin then
starting new pthread worker can violate CSP rules. Enabling
CROSS_ORIGIN uses an inline worker to instead load the worker script
indirectly using `importScripts`

Default value: false
10 changes: 10 additions & 0 deletions src/lib/libpthread.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,16 @@ var LibraryPThread = {
worker = new Worker(new URL('{{{ pthreadWorkerScript }}}', import.meta.url), {{{ pthreadWorkerOptions }}});
#else // EXPORT_ES6
var pthreadMainJs = _scriptName;
#if CROSS_ORIGIN && ENVIRONMENT_MAY_BE_WEB
// In order to support cross origin loading of worker threads load the
// worker via a tiny inline `importScripts` call. For some reason its
// fine to `importScripts` across origins, in cases where new Worker
// itself does not allow this.
// https://github.com/emscripten-core/emscripten/issues/21937
if (ENVIRONMENT_IS_WEB) {
pthreadMainJs = URL.createObjectURL(new Blob([`importScripts('${_scriptName}')`], { type: 'application/javascript' }));
}
#endif
#if expectToReceiveOnModule('mainScriptUrlOrBlob')
// We can't use makeModuleReceiveWithVar here since we want to also
// call URL.createObjectURL on the mainScriptUrlOrBlob.
Expand Down
6 changes: 6 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,12 @@ var GROWABLE_ARRAYBUFFERS = false;
// so we disable it by default to save on code size.
var WASM_JS_TYPES = false;

// If the emscripten-generated program is hosted on separate origin then
// starting new pthread worker can violate CSP rules. Enabling
// CROSS_ORIGIN uses an inline worker to instead load the worker script
// indirectly using `importScripts`
var CROSS_ORIGIN = false;

// For renamed settings the format is:
// [OLD_NAME, NEW_NAME]
// For removed settings (which now effectively have a fixed value and can no
Expand Down
42 changes: 41 additions & 1 deletion test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import unittest
import zlib
from functools import wraps
from http.server import BaseHTTPRequestHandler, HTTPServer
from http.server import BaseHTTPRequestHandler, HTTPServer, ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from urllib.request import urlopen

Expand Down Expand Up @@ -5631,6 +5631,46 @@ def test_rollup(self):
shutil.copy('hello.wasm', 'dist/')
self.run_browser('index.html', '/report_result?exit:0')

def test_cross_origin(self):
# Verfies that the emscripten-generted JS and Wasm can be hosted on a different origin.
# This test create a second HTTP server running on port 9999 that servers files from `subdir`.
# The main html is the servers from the normal 8888 server while the JS and Wasm are hosted
# on at 9999.
os.mkdir('subdir')
create_file('subdir/foo.txt', 'hello')
self.compile_btest('hello_world.c', ['-o', 'subdir/hello.js', '-sCROSS_ORIGIN', '-sPROXY_TO_PTHREAD', '-pthread', '-sEXIT_RUNTIME'])

class MyReqestHandler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory='subdir', **kwargs)

# Add COOP, COEP, CORP, and no-caching headers
def end_headers(self):
self.send_header('Accept-Ranges', 'bytes')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
self.send_header('Cross-Origin-Resource-Policy', 'cross-origin')

self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate, private, max-age=0')
self.send_header('Expires', '0')
self.send_header('Pragma', 'no-cache')
self.send_header('Vary', '*') # Safari insists on caching if this header is not present in addition to the above

return SimpleHTTPRequestHandler.end_headers(self)

create_file('test.html', '''
<script src="http://localhost:9999/hello.js"></script>
''')

server = HttpServerThread(ThreadingHTTPServer(('localhost', 9999), MyReqestHandler))
server.start()
try:
self.run_browser('test.html', '/report_result?exit:0')
finally:
server.stop()
server.join()


class emrun(RunnerCore):
def test_emrun_info(self):
Expand Down
Loading