Skip to content

Add DOM globals polyfill + native AbortController for Playground#1708

Open
bkaradzic-microsoft wants to merge 4 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:weekend/tpc-1579-dom-globals
Open

Add DOM globals polyfill + native AbortController for Playground#1708
bkaradzic-microsoft wants to merge 4 commits into
BabylonJS:masterfrom
bkaradzic-microsoft:weekend/tpc-1579-dom-globals

Conversation

@bkaradzic-microsoft
Copy link
Copy Markdown
Contributor

@bkaradzic-microsoft bkaradzic-microsoft commented May 18, 2026

Wires up native AbortController and adds JS polyfills for TextEncoder + PointerEvent so playgrounds depending on DOM globals stop ReferenceError'ing.

Changes:

  • New Apps/Playground/Scripts/dom_polyfill.js - self-detecting TextEncoder + PointerEvent polyfills.
  • Apps/Playground/Shared/AppContext.cpp - calls Babylon::Polyfills::AbortController::Initialize(env); loads dom_polyfill.js after file_polyfill.js.
  • Apps/Playground/CMakeLists.txt - adds dom_polyfill.js to SCRIPTS list; links AbortController plugin.
  • Apps/Playground/Android/BabylonNative/CMakeLists.txt - adds PRIVATE AbortController to BabylonNativeJNI link list (without this, Android builds fail with missing header - verified via CI).
  • Apps/Playground/Scripts/validation_native.js - extends document shim with createEvent + dispatchEvent.
  • Apps/Playground/Scripts/config.json - re-enables 1 test (idx 342).

Note: Polyfill foundation. Remaining affected playground tests need native dispatchEvent on canvas / Image polyfill / async-module support - out of scope here.

Landing context

This PR is one of 7 splits from the proven CI-green combined preview in draft PR #1702 (see #1702 for the full intended end-state and verified CI run 26044922430).

Note: the original split included an 8th PR (#1709, ES2020+ -> ES2019 syntax-repair polyfill for Chakra). It was closed in favour of investigating @babel/standalone properly (#1711).

Recommended landing order

Tier 1 - parallel-reviewable, no source conflicts:

  1. Fix ExternalTexture_OpenGL throw-stubs to avoid MSVC C4702 under /WX #1703 - ExternalTexture C4702 build fix
  2. Document accurate root cause for post-#1695 pixel-diff fallouts #1704 - config.json reason rewrites (5 entries)
  3. Document accurate root cause for 17 subtle pixel-diff tests #1705 - config.json reason rewrites (17 entries)

Tier 2 - sequential, each touches Apps/Playground/CMakeLists.txt SCRIPTS list + Apps/Playground/Shared/AppContext.cpp LoadScript order; rebase the next branch after the previous merges:

  1. Bump JsRuntimeHost for File/FileReader polyfill (re-enables 19 GLTF/OBJ tests) #1706 - File/Blob/FileReader polyfill (largest test impact: 19 re-enables)
  2. Add fetch() polyfill over XMLHttpRequest for Playground #1707 - fetch polyfill
  3. Add DOM globals polyfill + native AbortController for Playground #1708 - DOM globals + native AbortController + Android CMake link
  4. Add cubemap auto-expand polyfill for Playground (re-enables 7 PBR tests) #1710 - Cubemap auto-expand polyfill (loaded after babylon.max.js)

Reference policy reminder

Reference PNGs across all 7 PRs come from Babylon.js; never re-baked by BN. Combined diff: 0 PNGs.

bkaradzic-microsoft and others added 2 commits May 15, 2026 21:27
…nt.createEvent)


Playground tests that use modern browser APIs hit ReferenceErrors on
the Chakra-based BN runtime because TextEncoder and PointerEvent are
not part of older Chakra's built-ins. Several serializer tests also
exercise document.createEvent + element.dispatchEvent shapes that the
existing minimal document polyfill in validation_native.js did not
cover.

- Apps/Playground/Scripts/dom_polyfill.js: new self-detecting JS
  polyfills for TextEncoder (UTF-8 encode + encodeInto) and PointerEvent
  (constructor with the MouseEvent + PointerEvent surface).
- Apps/Playground/Shared/AppContext.cpp + CMakeLists.txt: wire in the
  native AbortController polyfill from JsRuntimeHost and load
  dom_polyfill.js before ammo.js.
- Apps/Playground/Scripts/validation_native.js: extend the existing
  document shim with createEvent, dispatchEvent, addEventListener and
  a generic-element fallback for createElement so click-event-based
  serializer tests can run.
- Apps/Playground/Scripts/config.json: re-enable "Serialize scene
  without materials" (idx 342), which now validates with 248 px diff.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AppContext.cpp includes <Babylon/Polyfills/AbortController.h> and calls
Babylon::Polyfills::AbortController::Initialize(env), but the Android-
specific CMakeLists at Apps/Playground/Android/BabylonNative did not
add AbortController to BabylonNativeJNI's PRIVATE link libraries. The
header is published only by the AbortController target, so Android CI
failed with 'fatal error: Babylon/Polyfills/AbortController.h file not
found'. Linux/macOS/Windows builds were fine because they use the main
Apps/Playground/CMakeLists.txt which already links AbortController.

Mirror the change there so Android picks up the include directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds missing DOM-like globals to the Playground JS host and wires up native AbortController so DOM-dependent playgrounds/tests stop throwing ReferenceError.

Changes:

  • Adds a self-detecting TextEncoder + PointerEvent polyfill script and loads it in the Playground startup sequence.
  • Initializes and links the native AbortController polyfill in Playground (including Android JNI link fix).
  • Extends the validation runner’s document shim with createEvent/dispatchEvent and re-enables one previously excluded test.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Apps/Playground/Shared/AppContext.cpp Initializes native AbortController and loads dom_polyfill.js during startup.
Apps/Playground/Scripts/validation_native.js Expands the document/element shims to support event creation/dispatch used by tests.
Apps/Playground/Scripts/dom_polyfill.js New polyfills for TextEncoder and PointerEvent when absent.
Apps/Playground/Scripts/config.json Re-enables one previously excluded serialization test.
Apps/Playground/CMakeLists.txt Packages dom_polyfill.js and links AbortController into Playground.
Apps/Playground/Android/BabylonNative/CMakeLists.txt Links AbortController into BabylonNativeJNI to fix Android builds.

Comment thread Apps/Playground/Scripts/dom_polyfill.js Outdated
Comment thread Apps/Playground/Scripts/dom_polyfill.js Outdated
Comment thread Apps/Playground/Scripts/dom_polyfill.js Outdated
- Replace `new Function('return this')` with a non-eval globalThis/self/
  window/global resolution chain; keep the Function-constructor trick
  only as a last-resort fallback for runtimes that ship none of the
  standard globals.
- TextEncoder.prototype.encodeInto: normalize `input` (undefined ->
  empty string) before computing `read`, so the returned read count
  stays consistent with the bytes written by `encode()` (previously
  `String(undefined).length` returned 9 for a zero-byte write).
- PointerEvent.cancelable: default to `false` (DOM spec EventInit
  default) instead of `true`; callers that need a cancelable event
  must pass `cancelable: true` explicitly.

Refs PR BabylonJS#1708.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@ryantrem ryantrem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this makes the tests work, but if a consumer tried to use the same things in their own app, they wouldn't work. Is that really the state we want to be in, or we want to know what things don't work because they aren't polyfilled? I would have thought we would either document these features don't work because we don't polyfill, or we add native polyfills for them.

bkaradzic-microsoft added a commit that referenced this pull request May 21, 2026
Per-test PIL-composite triage of all 17 ``subtle pixel-diff`` tests.
None of them are deterministic-cosmetic (no re-bakes possible - all show
real visible regressions).

Updates the `reason` field in `Apps/Playground/Scripts/config.json` for
17 tests with accurate symptom descriptions, classifying into recurring
root-cause clusters:

- 9 GUI green->red color regressions (idx 160, 174, 175, 196, 197, 370,
402, 566, 587)
- 4 OpenPBR analytic-lights right-column red rendering (580, 584, 587,
592)
- 1 instanced billboard foliage red (169)
- 1 LineEdgesRenderer extra red lines (179)
- 1 Background blur red splotches (602)
- 1 Clip planes GUI sliders red (182)
- 1 Instanced Bones edge-AA (256, borderline)

**No source changes, no test re-enables, no PNGs.** Metadata-only
correction so the issue tracker reflects actionable root causes.
---

## Landing context

This PR is one of **7 splits** from the proven CI-green combined preview
in **draft PR #1702** (see
[#1702](#1702) for the
full intended end-state and verified CI run
[26044922430](https://github.com/BabylonJS/BabylonNative/actions/runs/26044922430)).

> Note: the original split included an 8th PR (#1709, ES2020+ -> ES2019
syntax-repair polyfill for Chakra). It was closed in favour of
investigating `@babel/standalone` properly (#1711).

### Recommended landing order

**Tier 1 - parallel-reviewable, no source conflicts:**
1. #1703 - ExternalTexture C4702 build fix
2. #1704 - config.json `reason` rewrites (5 entries)
3. #1705 - config.json `reason` rewrites (17 entries)

**Tier 2 - sequential, each touches `Apps/Playground/CMakeLists.txt`
SCRIPTS list + `Apps/Playground/Shared/AppContext.cpp` LoadScript order;
rebase the next branch after the previous merges:**

4. #1706 - File/Blob/FileReader polyfill (largest test impact: 19
re-enables)
5. #1707 - fetch polyfill
6. #1708 - DOM globals + native AbortController + Android CMake link
7. #1710 - Cubemap auto-expand polyfill (loaded after babylon.max.js)

### Reference policy reminder

Reference PNGs across all 7 PRs come from Babylon.js; never re-baked by
BN. Combined diff: **0 PNGs**.
bkaradzic-microsoft added a commit that referenced this pull request May 21, 2026
Per-test triage of 5 post-#1695 pixel-diff fallouts. Updates the
`reason` field in `Apps/Playground/Scripts/config.json` for 3 entries to
name the real BN rendering regression instead of generic "Pixel
comparison fails":

- idx 363 SSR2 - SSR not rendering wet floor.
- idx 369 Sprites Pixel Perfect - sprite alpha-blending broken.
- idx 395 soft-transparent-shadows - soft-shadow filter precision
degraded.

**No source changes, no test re-enables, no PNGs.** Metadata-only
correction so the issue tracker reflects actionable root causes for
follow-up engineering work.
---

## Landing context

This PR is one of **7 splits** from the proven CI-green combined preview
in **draft PR #1702** (see
[#1702](#1702) for the
full intended end-state and verified CI run
[26044922430](https://github.com/BabylonJS/BabylonNative/actions/runs/26044922430)).

> Note: the original split included an 8th PR (#1709, ES2020+ -> ES2019
syntax-repair polyfill for Chakra). It was closed in favour of
investigating `@babel/standalone` properly (#1711).

### Recommended landing order

**Tier 1 - parallel-reviewable, no source conflicts:**
1. #1703 - ExternalTexture C4702 build fix
2. #1704 - config.json `reason` rewrites (5 entries)
3. #1705 - config.json `reason` rewrites (17 entries)

**Tier 2 - sequential, each touches `Apps/Playground/CMakeLists.txt`
SCRIPTS list + `Apps/Playground/Shared/AppContext.cpp` LoadScript order;
rebase the next branch after the previous merges:**

4. #1706 - File/Blob/FileReader polyfill (largest test impact: 19
re-enables)
5. #1707 - fetch polyfill
6. #1708 - DOM globals + native AbortController + Android CMake link
7. #1710 - Cubemap auto-expand polyfill (loaded after babylon.max.js)

### Reference policy reminder

Reference PNGs across all 7 PRs come from Babylon.js; never re-baked by
BN. Combined diff: **0 PNGs**.
bkaradzic-microsoft added a commit to bkaradzic-microsoft/BabylonNative that referenced this pull request May 27, 2026
…interEvent polyfills

Addresses Ryan's review (pullrequestreview-4312723810):
> I would have thought we would either document these features don't
> work because we don't polyfill, or we add native polyfills for them.

The previous version of this PR added a Playground-only dom_polyfill.js
that injected TextEncoder and PointerEvent shims onto the global object.
That worked for the Playground but every other app embedding Babylon
Native against Chakra would have had to independently re-discover and
ship the same workaround. Move both into proper C++ polyfill libraries
under Polyfills/ with the same Initialize(Napi::Env) shape as the
existing Polyfills/TextDecoder (the canonical reference for the pattern
in jsruntimehost). Now any BN consumer that links them and calls
Initialize() gets the same behaviour for free.

- Polyfills/TextEncoder/ - Napi::ObjectWrap implementation of the
  WHATWG Encoding Standard TextEncoder surface: encoding accessor
  (always utf-8), encode(input) returning a fresh Uint8Array of UTF-8
  bytes via Napi::String::Utf8Value() (the engine does the UTF-16 to
  UTF-8 conversion natively, which is well-tested across all N-API
  surfaces), and encodeInto(source, destination) which walks the UTF-8
  byte stream a code-point at a time, stops cleanly on a code-point
  boundary when the destination fills up, and derives the spec-required
  UTF-16 read code-unit count from the UTF-8 leading-byte bit pattern
  (1 code unit for 1/2/3-byte sequences, 2 for the surrogate pair
  encoded by a 4-byte sequence). Avoids Utf16Value() entirely to
  sidestep N-API shim quirks.

- Polyfills/PointerEvent/ - Napi::ObjectWrap implementation of the W3C
  Pointer Events Level 3 surface. The constructor materialises the
  same ~35 MouseEvent + PointerEvent own properties on the receiver
  that the prior JS shim used (matching defaults: cancelable = false
  per DOM EventInit spec, pressure = 0.5, isPrimary = true unless
  explicitly disabled, etc.) and exposes preventDefault /
  stopPropagation / stopImmediatePropagation / getModifierState /
  getCoalescedEvents / getPredictedEvents as InstanceMethods.

CMake / wiring:
- Top-level CMakeLists.txt: add BABYLON_NATIVE_POLYFILL_TEXTENCODER and
  BABYLON_NATIVE_POLYFILL_POINTEREVENT options (default ON).
- Polyfills/CMakeLists.txt: add_subdirectory for each, guarded by their
  option.
- Apps/Playground/CMakeLists.txt and
  Apps/Playground/Android/BabylonNative/CMakeLists.txt: PRIVATE link
  TextEncoder + PointerEvent so AppContext.cpp can use the published
  headers on every platform.
- Apps/Playground/Shared/AppContext.cpp: include the new headers, call
  TextEncoder::Initialize(env) and PointerEvent::Initialize(env) inside
  the runtime Dispatch lambda right after the existing
  TextDecoder::Initialize call (before Babylon.js scripts are loaded),
  and drop the LoadScript for dom_polyfill.js now that the shims are
  part of the runtime itself.
- Delete Apps/Playground/Scripts/dom_polyfill.js (no longer used).

Verified locally on Chakra build (build/win32 RelWithDebInfo, headless):

- 'Serialize scene without materials' (the test the prior JS shim was
  added to re-enable) validates with 248 px diff - same as before.
- 'GLTF Serializer multimaterial with raw texture' validates with 891
  px diff (matches the JS-shim baseline exactly).
- EXR Loader, Native Canvas, GUI Input Test all behave identically to
  the JS-shim baseline (validated / skipped same as before).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…interEvent polyfills

Addresses Ryan's review (pullrequestreview-4312723810):
> I would have thought we would either document these features don't
> work because we don't polyfill, or we add native polyfills for them.

The previous version of this PR added a Playground-only dom_polyfill.js
that injected TextEncoder and PointerEvent shims onto the global object.
That worked for the Playground but every other app embedding Babylon
Native against Chakra would have had to independently re-discover and
ship the same workaround. Move both into proper C++ polyfill libraries
under Polyfills/ with the same Initialize(Napi::Env) shape as the
existing Polyfills/TextDecoder (the canonical reference for the pattern
in jsruntimehost). Now any BN consumer that links them and calls
Initialize() gets the same behaviour for free.

- Polyfills/TextEncoder/ - Napi::ObjectWrap implementation of the
  WHATWG Encoding Standard TextEncoder surface: encoding accessor
  (always utf-8), encode(input) returning a fresh Uint8Array of UTF-8
  bytes via Napi::String::Utf8Value() (the engine does the UTF-16 to
  UTF-8 conversion natively, which is well-tested across all N-API
  surfaces), and encodeInto(source, destination) which walks the UTF-8
  byte stream a code-point at a time, stops cleanly on a code-point
  boundary when the destination fills up, and derives the spec-required
  UTF-16 read code-unit count from the UTF-8 leading-byte bit pattern
  (1 code unit for 1/2/3-byte sequences, 2 for the surrogate pair
  encoded by a 4-byte sequence). Avoids Utf16Value() entirely to
  sidestep N-API shim quirks.

- Polyfills/PointerEvent/ - Napi::ObjectWrap implementation of the W3C
  Pointer Events Level 3 surface. The constructor materialises the
  same ~35 MouseEvent + PointerEvent own properties on the receiver
  that the prior JS shim used (matching defaults: cancelable = false
  per DOM EventInit spec, pressure = 0.5, isPrimary = true unless
  explicitly disabled, etc.) and exposes preventDefault /
  stopPropagation / stopImmediatePropagation / getModifierState /
  getCoalescedEvents / getPredictedEvents as InstanceMethods.

CMake / wiring:
- Top-level CMakeLists.txt: add BABYLON_NATIVE_POLYFILL_TEXTENCODER and
  BABYLON_NATIVE_POLYFILL_POINTEREVENT options (default ON).
- Polyfills/CMakeLists.txt: add_subdirectory for each, guarded by their
  option.
- Apps/Playground/CMakeLists.txt and
  Apps/Playground/Android/BabylonNative/CMakeLists.txt: PRIVATE link
  TextEncoder + PointerEvent so AppContext.cpp can use the published
  headers on every platform.
- Apps/Playground/Shared/AppContext.cpp: include the new headers, call
  TextEncoder::Initialize(env) and PointerEvent::Initialize(env) inside
  the runtime Dispatch lambda right after the existing
  TextDecoder::Initialize call (before Babylon.js scripts are loaded),
  and drop the LoadScript for dom_polyfill.js now that the shims are
  part of the runtime itself.
- Delete Apps/Playground/Scripts/dom_polyfill.js (no longer used).

Verified locally on Chakra build (build/win32 RelWithDebInfo, headless):

- 'Serialize scene without materials' (the test the prior JS shim was
  added to re-enable) validates with 248 px diff - same as before.
- 'GLTF Serializer multimaterial with raw texture' validates with 891
  px diff (matches the JS-shim baseline exactly).
- EXR Loader, Native Canvas, GUI Input Test all behave identically to
  the JS-shim baseline (validated / skipped same as before).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bkaradzic-microsoft bkaradzic-microsoft force-pushed the weekend/tpc-1579-dom-globals branch from c98d68d to 2101ef8 Compare May 27, 2026 22:08
bkaradzic-microsoft added a commit that referenced this pull request May 28, 2026
…1703)

Fixes MSVC C4702 (unreachable code) under `/WX` in
`Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp` so the
OpenGL TU builds cleanly without the project-wide `/wd4702` workaround.

## What changed

`Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp` keeps the
unimplemented `Impl` stubs as `throw std::runtime_error{"not
implemented"}` (so callers get an unambiguous diagnostic matching every
other backend's unimplemented path) and wraps the `#include
"ExternalTexture_Shared.h"` line in `#pragma warning(push)/(disable:
4702)/(pop)`. The shared dispatchers instantiated by that include
unconditionally call the stubs, so MSVC's flow analysis flags the
dispatcher's post-call code as unreachable; the pragma suppresses C4702
only for the dispatcher code instantiated in this translation unit. Any
other code in this file is still subject to `/WX` C4702 enforcement.

`Plugins/ExternalTexture/CMakeLists.txt` - drop the `/wd4702` target
compile option, which previously silenced C4702 across the whole target.

## Alternatives considered

- **`[[noreturn]]` on the stubs**: MSVC propagates "never returns"
through the shared dispatcher, which flags *more* post-call statements
as unreachable. Tried and rejected (it made the warning worse).
- **TU-wide `/wd4702` via `target_compile_options`**: silences any
future legitimate C4702 elsewhere in the same TU, defeating `/WX`. The
current localized `#pragma warning(push/pop)` block keeps the rest of
the TU under `/WX` enforcement.
- **Replacing the throws with inert return-default stubs**: changes
product behaviour (callers would silently receive a null texture instead
of a clear "not implemented" error) to work around a compiler warning.
Rejected on principle.

## Verified

Clean under Debug + Release + RelWithDebInfo on OpenGL (`win32-gl`,
`GRAPHICS_API=OpenGLWindowsDevOnly`) and D3D11 (`win32`).

---

## Landing context

This PR is one of **7 splits** from the proven CI-green combined preview
in **draft PR #1702** (see
[#1702](#1702) for the
full intended end-state and verified CI run
[26044922430](https://github.com/BabylonJS/BabylonNative/actions/runs/26044922430)).

> Note: the original split included an 8th PR (#1709, ES2020+ -> ES2019
syntax-repair polyfill for Chakra). It was closed in favour of
investigating `@babel/standalone` properly (#1711).

### Recommended landing order

**Tier 1 - parallel-reviewable, no source conflicts:**
1. #1703 - ExternalTexture C4702 build fix
2. #1704 - config.json `reason` rewrites (5 entries)
3. #1705 - config.json `reason` rewrites (17 entries)

**Tier 2 - sequential, each touches `Apps/Playground/CMakeLists.txt`
SCRIPTS list + `Apps/Playground/Shared/AppContext.cpp` LoadScript order;
rebase the next branch after the previous merges:**

4. #1706 - File/Blob/FileReader polyfill (largest test impact: 19
re-enables)
5. #1707 - fetch polyfill
6. #1708 - DOM globals + native AbortController + Android CMake link
7. #1710 - Cubemap auto-expand polyfill (loaded after babylon.max.js)

### Reference policy reminder

Reference PNGs across all 7 PRs come from Babylon.js; never re-baked by
BN. Combined diff: **0 PNGs**.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@bghgary bghgary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Reviewed by Copilot on behalf of @bghgary]

TextEncoder belongs in JsRuntimeHost. No DOM polyfills.

@@ -0,0 +1,17 @@
#pragma once
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextEncoder belongs in JsRuntimeHost alongside TextDecoder (also WHATWG Encoding Standard). Splitting them across repos means non-BN consumers can't get TextEncoder without duplicating.

@@ -0,0 +1,21 @@
#pragma once
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't polyfill DOM types. BabylonNative isn't a browser; DeviceInputSystem already abstracts input. Let's discuss offline before adding this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants