diff --git a/webgl/build.sh b/webgl/build.sh index d3b38ff..92fd036 100755 --- a/webgl/build.sh +++ b/webgl/build.sh @@ -11,6 +11,7 @@ # ./build.sh --clean # make clean + wipe CMake cache first (full rebuild) # ./build.sh --slow # serial build (plain make, one file at a time) # ./build.sh encoder -DKTX2_ZSTANDARD=OFF -DCMAKE_BUILD_TYPE=Debug +# ./build.sh -DNO_DYNAMIC_EXECUTION=ON # no eval/new Function (strict CSP) # # Any -D... argument is forwarded to cmake. Runnable from any directory. # @@ -35,7 +36,7 @@ for a in "$@"; do --slow) SLOW=1 ;; transcoder|encoder) TARGETS+=("$a") ;; -D*) CMAKE_ARGS+=("$a") ;; - -h|--help) sed -n '3,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + -h|--help) sed -n '3,16p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) die "unknown argument: '$a' (try --help)" ;; esac done diff --git a/webgl/encoder/CMakeLists.txt b/webgl/encoder/CMakeLists.txt index 846cdea..478c0cd 100644 --- a/webgl/encoder/CMakeLists.txt +++ b/webgl/encoder/CMakeLists.txt @@ -5,8 +5,18 @@ project(basisu_encoder_js) option(KTX2_ZSTANDARD "Enable KTX2 Zstandard support" TRUE) option(SUPPORT_ASTCENC "Enable astcenc support" FALSE) +# Build JavaScript glue that never calls eval() or new Function(), so the module +# loads under a Content-Security-Policy without 'unsafe-eval' (OFF by default). +option(NO_DYNAMIC_EXECUTION "Disallow eval()/new Function() in the JS glue" FALSE) + +# With NO_DYNAMIC_EXECUTION, generate the embind call wrappers at build time (fast) +# instead of using the slower generic wrappers. No effect unless NO_DYNAMIC_EXECUTION is ON. +option(EMBIND_AOT "Generate embind wrappers at build time" TRUE) + message("KTX2_ZSTANDARD=${KTX2_ZSTANDARD}") message("SUPPORT_ASTCENC=${SUPPORT_ASTCENC}") +message("NO_DYNAMIC_EXECUTION=${NO_DYNAMIC_EXECUTION}") +message("EMBIND_AOT=${EMBIND_AOT}") # Only for Emscripten builds. if(EMSCRIPTEN) @@ -132,6 +142,13 @@ if(EMSCRIPTEN) # Base link flags set(LINK_BASE "--bind -s ALLOW_MEMORY_GROWTH=1 -s INITIAL_MEMORY=134217728 -s STACK_SIZE=2097152 -s MODULARIZE=1 -s EXPORT_NAME=BASIS -s EXPORTED_RUNTIME_METHODS=['HEAP8']") + if(NO_DYNAMIC_EXECUTION) + set(LINK_BASE "${LINK_BASE} -s DYNAMIC_EXECUTION=0") + if(EMBIND_AOT) + set(LINK_BASE "${LINK_BASE} -s EMBIND_AOT=1") + endif() + endif() + set(LINK_THREADS "-s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=32 -s ENVIRONMENT=web,worker") set(LINK_WASM64 "-s MEMORY64=1 -sWASM_BIGINT=1 --profiling-funcs") diff --git a/webgl/encoder/README.md b/webgl/encoder/README.md index 2055d02..f884434 100644 --- a/webgl/encoder/README.md +++ b/webgl/encoder/README.md @@ -5,3 +5,22 @@ cd webgl/encoder/build/ emcmake cmake ../ make ``` + +### Strict Content-Security-Policy (no `unsafe-eval`) + +By default, the generated JavaScript glue uses `new Function()` to build its +embind bindings at runtime. A Content-Security-Policy without `'unsafe-eval'` +blocks this, and module startup fails with an `EvalError`. To build glue that +never calls `eval()` or `new Function()`, add `-DNO_DYNAMIC_EXECUTION=ON`: + +```shell +emcmake cmake ../ -DNO_DYNAMIC_EXECUTION=ON +make +``` + +This adds `-s DYNAMIC_EXECUTION=0 -s EMBIND_AOT=1` to the link flags. The +`.wasm` file does not change. `EMBIND_AOT` generates the bindings at build +time, so JavaScript-to-WASM calls stay as fast as in the default build. The +page still needs `'wasm-unsafe-eval'` in `script-src` to compile the +WebAssembly module. Use `-DEMBIND_AOT=OFF` to get the smaller, generic +bindings, which are slower per call. diff --git a/webgl/transcoder/CMakeLists.txt b/webgl/transcoder/CMakeLists.txt index 159f87a..8703250 100644 --- a/webgl/transcoder/CMakeLists.txt +++ b/webgl/transcoder/CMakeLists.txt @@ -10,8 +10,18 @@ option(KTX2 "KTX2" TRUE) # If you know you'll never be transcoding KTX2 files using this feature you can set KTX2_ZSTANDARD to 0. option(KTX2_ZSTANDARD "KTX2_ZSTANDARD" TRUE) +# Use "cmake -DNO_DYNAMIC_EXECUTION=TRUE" to build JavaScript glue that never calls eval() or new Function(). +# This lets the module load under a Content-Security-Policy without 'unsafe-eval'. +option(NO_DYNAMIC_EXECUTION "NO_DYNAMIC_EXECUTION" FALSE) + +# With NO_DYNAMIC_EXECUTION, generate the embind call wrappers at build time (fast) instead of +# using the slower generic wrappers. Only has an effect when NO_DYNAMIC_EXECUTION is TRUE. +option(EMBIND_AOT "EMBIND_AOT" TRUE) + message("KTX2=${KTX2}") message("KTX2_ZSTANDARD=${KTX2_ZSTANDARD}") +message("NO_DYNAMIC_EXECUTION=${NO_DYNAMIC_EXECUTION}") +message("EMBIND_AOT=${EMBIND_AOT}") if (EMSCRIPTEN) set(CMAKE_CXX_STANDARD 17) @@ -61,8 +71,17 @@ if (EMSCRIPTEN) target_include_directories(basis_transcoder.js PRIVATE ../../transcoder) + set(TRANSCODER_LINK_FLAGS "--bind -s ALLOW_MEMORY_GROWTH=1 -O3 -s MALLOC=emmalloc -s MODULARIZE=1 -s EXPORT_NAME=BASIS -s ASSERTIONS=0 -s EXPORTED_RUNTIME_METHODS=['HEAP8']") + + if (NO_DYNAMIC_EXECUTION) + set(TRANSCODER_LINK_FLAGS "${TRANSCODER_LINK_FLAGS} -s DYNAMIC_EXECUTION=0") + if (EMBIND_AOT) + set(TRANSCODER_LINK_FLAGS "${TRANSCODER_LINK_FLAGS} -s EMBIND_AOT=1") + endif() + endif() + set_target_properties(basis_transcoder.js PROPERTIES OUTPUT_NAME "basis_transcoder" SUFFIX ".js" - LINK_FLAGS "--bind -s ALLOW_MEMORY_GROWTH=1 -O3 -s MALLOC=emmalloc -s MODULARIZE=1 -s EXPORT_NAME=BASIS -s ASSERTIONS=0 -s EXPORTED_RUNTIME_METHODS=['HEAP8']") + LINK_FLAGS "${TRANSCODER_LINK_FLAGS}") endif() diff --git a/webgl/transcoder/README.md b/webgl/transcoder/README.md index 1553a3a..551c43a 100644 --- a/webgl/transcoder/README.md +++ b/webgl/transcoder/README.md @@ -7,3 +7,22 @@ cd webgl/transcoder/build/ emcmake cmake ../ make ``` + +### Strict Content-Security-Policy (no `unsafe-eval`) + +By default, the generated JavaScript glue uses `new Function()` to build its +embind bindings at runtime. A Content-Security-Policy without `'unsafe-eval'` +blocks this, and module startup fails with an `EvalError`. To build glue that +never calls `eval()` or `new Function()`, add `-DNO_DYNAMIC_EXECUTION=ON`: + +```shell +emcmake cmake ../ -DNO_DYNAMIC_EXECUTION=ON +make +``` + +This adds `-s DYNAMIC_EXECUTION=0 -s EMBIND_AOT=1` to the link flags. The +`.wasm` file does not change. `EMBIND_AOT` generates the bindings at build +time, so JavaScript-to-WASM calls stay as fast as in the default build. The +page still needs `'wasm-unsafe-eval'` in `script-src` to compile the +WebAssembly module. Use `-DEMBIND_AOT=OFF` to get the smaller, generic +bindings, which are slower per call. diff --git a/webgl/transcoder/basis_wrappers.cpp b/webgl/transcoder/basis_wrappers.cpp index c80d4df..ac8ec88 100644 --- a/webgl/transcoder/basis_wrappers.cpp +++ b/webgl/transcoder/basis_wrappers.cpp @@ -3347,17 +3347,17 @@ EMSCRIPTEN_BINDINGS(basis_codec) { constant("ASTC_HDR_MAX_VAL", basist::ASTC_HDR_MAX_VAL); // UASTC LDR/HDR flags/options - constant("cPackUASTCLevelFastest", cPackUASTCLevelFastest); - constant("cPackUASTCLevelFaster", cPackUASTCLevelFaster); - constant("cPackUASTCLevelDefault", cPackUASTCLevelDefault); - constant("cPackUASTCLevelSlower", cPackUASTCLevelSlower); - constant("cPackUASTCLevelVerySlow", cPackUASTCLevelVerySlow); - constant("cPackUASTCLevelMask", cPackUASTCLevelMask); - constant("cPackUASTCFavorUASTCError", cPackUASTCFavorUASTCError); - constant("cPackUASTCFavorBC7Error", cPackUASTCFavorBC7Error); - constant("cPackUASTCETC1FasterHints", cPackUASTCETC1FasterHints); - constant("cPackUASTCETC1FastestHints", cPackUASTCETC1FastestHints); - constant("cPackUASTCETC1DisableFlipAndIndividual", cPackUASTCETC1DisableFlipAndIndividual); + constant("cPackUASTCLevelFastest", static_cast(cPackUASTCLevelFastest)); + constant("cPackUASTCLevelFaster", static_cast(cPackUASTCLevelFaster)); + constant("cPackUASTCLevelDefault", static_cast(cPackUASTCLevelDefault)); + constant("cPackUASTCLevelSlower", static_cast(cPackUASTCLevelSlower)); + constant("cPackUASTCLevelVerySlow", static_cast(cPackUASTCLevelVerySlow)); + constant("cPackUASTCLevelMask", static_cast(cPackUASTCLevelMask)); + constant("cPackUASTCFavorUASTCError", static_cast(cPackUASTCFavorUASTCError)); + constant("cPackUASTCFavorBC7Error", static_cast(cPackUASTCFavorBC7Error)); + constant("cPackUASTCETC1FasterHints", static_cast(cPackUASTCETC1FasterHints)); + constant("cPackUASTCETC1FastestHints", static_cast(cPackUASTCETC1FastestHints)); + constant("cPackUASTCETC1DisableFlipAndIndividual", static_cast(cPackUASTCETC1DisableFlipAndIndividual)); constant("UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO", UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO); constant("UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH", UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH);