Spec-Conformer Script Execution Pipeline — Inline Isolation + Chunk Queue + Streaming Parser #37

Closed
opened 2026-06-17 21:03:31 +00:00 by Artur · 1 comment
Owner

Spezifikationskonforme Script Execution Pipeline

Problembeschreibung

Drei Discord-interne Fehler verhindern dass discord.com/app vollständig in unserer Engine läuft:

  1. __OVERLAY__ is not defined — Inline-Config-Script setzt Wert auf Happy DOMs window, nicht auf Proxy-Window
  2. window.GLOBAL_ENV.RELEASE_CHANNEL — Selbe Root-Cause: Inline-Scripts laufen in falschem Kontext
  3. Cannot access 'K' before initialization — Webpack-Chunks executieren in fetch-Complete-Reihenfolge statt DOM-Insertion-Reihenfolge → TDZ

Alle drei sind Artefakte unserer Engine, keine echten Browser-Fehler — Discord läuft fehlerfrei in Chrome/Firefox.


Root-Cause Analyse

Phase A — Inline Script Isolation (Errors 1 & 2)

Aktueller Flow in parser.ts:

parser.start():
  Step 1: preloadScripts(html)     ← Regex scannt ALLE <script> Tags
  Step 2: document.write(html)     ← Happy DOM parsed + evaluiert inline scripts
    └─ connectedToDocument-Hook:
       ├─ src vorhanden → queueDynamicScript() ✅
       └─ KEIN src (inline) → Happy DOM Eval ✗ → Werte auf FALSCHEM window
  Step 3-4: onScriptTag(script)    ← Erneute Execution via ExecutionRealm
    └─ Double Execution! Zweiter Durchlauf korrigiert Proxy, aber:
       - var-Deklarationen sind function-scoped → leaken nicht
       - Side-Effekte feuern doppelt

Konsequenz: __OVERLAY__ und GLOBAL_ENV landen auf Happy DOMs internem window, während external scripts (webpack etc.) via ExecutionRealm.execute() auf dem Proxy-Window laufen und die Config nicht sehen.

Phase B — Parser-Execution Integration

Aktuell: Komplettes HTML wird vorausgescannt → document.write() → alle Scripts nachträglich. Spezifikationswidrig weil:

  • Inline-Scripts sollen während des Parsings ausgeführt werden (blocking)
  • External blocking Scripts sollen den Parser pausieren
  • Deferred Scripts sollen nach dem Parsing in Dokument-Reihenfolge laufen

Phase C — Dynamic Chunk Execution Order (Error 3)

Aktuell in dynamic-scripts.ts:

queueDynamicScript(chunkA) → loadAndExecute(A) startet async
queueDynamicScript(chunkB) → loadAndExecute(B) startet async
   ↓ beide await fetchContent() parallel
→ B's fetch completed first! → executeRaw(B) → dispatchEvent('load')
→ A completed → executeRaw(A)
// REIHENFOLGE VERTAUSCHT → TDZ: 'K' aus A noch nicht initialisiert

Spezifikation: Browser verarbeitet Chunks in DOM-Insertion-Reihenfolge. Script-Evaluation ist synchron pro Chunk, nur das Netzwerk-Fetching ist parallel.


Lösungsansatz

Phase A: Inline Script Isolation

Änderungen in parser.ts:

  1. start() Methode: HTML-Template-Modus — Happy DOMs Script-Evaluation während document.write() unterdrücken
  2. executeScriptsInOrder(): Alle Scripts (inline + external blocking) direkt via ScriptLoader.onScriptTag() ausführen, in Dokument-Reihenfolge
  3. Inline-Scripts laufen NIEMALS in Happy DOMs Window — nur via ExecutionRealm

Änderungen in dynamic-scripts.ts:
4. patchConnectedToDocument(): Inline-Scripts nicht mehr an Happy DOM delegieren
5. disableEvaluation bei inline scripts setzen

Änderungen in execution-realm.ts:
6. Strict-Mode-Stripping vor Ausführung (verhindert dass "use strict" this-Binding zerstört)

Phase B: Parser-Execution Integration

Änderungen in parser.ts:
7. HTML in Chunks parsen, Script-Token isolieren
8. Bei inline/blocking script: Parser pausieren → Script ausführen → Parser fortsetzen
9. Parser-Resume Mechanismus

Phase C: Dynamic Script Execution Queue

Änderungen in dynamic-scripts.ts:
10. chunkQueue — FIFO-Queue für dynamische Scripts
11. processQueue() — sequentielle Verarbeitung (fetch parallel, execute serial)
12. loadAndExecute() → Queue-Entry statt eigenständiger Prozess


Akzeptanzkriterien

  • Inline-Script __OVERLAY__ = false setzt Wert auf Proxy-Window → visible für External-Scripts
  • Inline-Script window.GLOBAL_ENV = {...} setzt Wert auf Proxy-Window
  • Alle bestehenden 1300 Tests passieren weiterhin (0 Regression)
  • Discord-lädt ohne __OVERLAY__ is not defined Fehler
  • Discord-lädt ohne GLOBAL_ENV.RELEASE_CHANNEL Fehler
  • Webpack-Chunks executieren in DOM-Insertion-Reihenfolge → kein Cannot access 'K' TDZ
  • Inline-Script-Side-Effekte feuern genau einmal (kein Double-Execution)
  • Deferred-Scripts laufen in Dokument-Reihenfolge
  • Neue Unit-Tests decken alle drei Phasen ab:
    • Inline-Script-Isolation (Proxy Window sichtbarkeit)
    • Chunk-Execution-Reihenfolge (Queue-Verhalten)
    • Kein Double-Execution
    • Deferred-Script-Ordering

Betroffene Dateien

Datei Phase Änderung
src/dom/parser.ts A+B start() umbauen, _disableScriptEvaluation(), executeScriptsInOrder()
src/js/dynamic-scripts.ts A+C patchConnectedToDocument() fix, chunkQueue + processQueue()
src/js/execution-realm.ts A Strict-Mode-Stripping
src/js/script-loader.ts A+B executeInline() ggf. anpassen
tests/unit/dynamic-scripts.test.ts C Chunk-Ordering-Tests
tests/unit/execution-realm.test.ts A Inline-Isolation-Tests
neu: tests/unit/script-pipeline.test.ts A+B+C Integration-Tests für gesamte Pipeline

Risiken

Risiko Phase Mitigation
Happy DOM's document.write() erwartet inline execution A _disableScriptEvaluation als Flag; langfristig eigenen Parser
Double-Execution durch Pre-Scan + document.write A Alle inline scripts via ExecutionRealm, nicht Happy DOM
Reentrancy in Chunk-Queue (onload triggered neuen Chunk) C Queue appended hinten — kein Reentrancy-Problem
Strict-Mode-Stripping entfernt legitime "use strict" A Nur top-level directive entfernen, nicht in Funktionen
Performance durch serialisierte Chunk-Execution C Fetch bleibt parallel — nur execute serial (real browser genauso)

Testplan

Unit-Tests:

  1. inline script sets window.__OVERLAY__ — nach Execution über Proxy lesbar
  2. inline script sets window.GLOBAL_ENV — via evaluate() lesbar
  3. inline script runs exactly once — Zähler vorher/nachher
  4. chunk queue executes in insertion order — 3 chunks mit sequentiellen IDs
  5. chunk queue still loads concurrently — fetch-Zeit messen (parallel)
  6. deferred scripts execute in document order — Reihenfolge der Ausführung
  7. no double execution for pre-scanned scripts — Parser + DynamicHandler

Integration-Tests:
8. full page load with inline config + external scripts — simuliert Discord-Szenario
9. dynamic webpack chunks maintain correct TDZ ordering — zirkuläre Module simulieren
10. regression: alle 1300+ bestehenden Tests — voller Suite

E2E:
11. discord.com/app — lädt ohne die 3 Engine-Fehler (manuell / CI)

## Spezifikationskonforme Script Execution Pipeline ### Problembeschreibung Drei Discord-interne Fehler verhindern dass discord.com/app vollständig in unserer Engine läuft: 1. **`__OVERLAY__ is not defined`** — Inline-Config-Script setzt Wert auf Happy DOMs `window`, nicht auf Proxy-Window 2. **`window.GLOBAL_ENV.RELEASE_CHANNEL`** — Selbe Root-Cause: Inline-Scripts laufen in falschem Kontext 3. **`Cannot access 'K' before initialization`** — Webpack-Chunks executieren in fetch-Complete-Reihenfolge statt DOM-Insertion-Reihenfolge → TDZ Alle drei sind **Artefakte unserer Engine**, keine echten Browser-Fehler — Discord läuft fehlerfrei in Chrome/Firefox. --- ### Root-Cause Analyse #### Phase A — Inline Script Isolation (Errors 1 & 2) **Aktueller Flow** in `parser.ts`: ``` parser.start(): Step 1: preloadScripts(html) ← Regex scannt ALLE <script> Tags Step 2: document.write(html) ← Happy DOM parsed + evaluiert inline scripts └─ connectedToDocument-Hook: ├─ src vorhanden → queueDynamicScript() ✅ └─ KEIN src (inline) → Happy DOM Eval ✗ → Werte auf FALSCHEM window Step 3-4: onScriptTag(script) ← Erneute Execution via ExecutionRealm └─ Double Execution! Zweiter Durchlauf korrigiert Proxy, aber: - var-Deklarationen sind function-scoped → leaken nicht - Side-Effekte feuern doppelt ``` **Konsequenz:** `__OVERLAY__` und `GLOBAL_ENV` landen auf Happy DOMs internem `window`, während external scripts (webpack etc.) via `ExecutionRealm.execute()` auf dem Proxy-Window laufen und die Config nicht sehen. #### Phase B — Parser-Execution Integration **Aktuell:** Komplettes HTML wird vorausgescannt → `document.write()` → alle Scripts nachträglich. Spezifikationswidrig weil: - Inline-Scripts sollen **während des Parsings** ausgeführt werden (blocking) - External blocking Scripts sollen den Parser **pausieren** - Deferred Scripts sollen **nach dem Parsing** in Dokument-Reihenfolge laufen #### Phase C — Dynamic Chunk Execution Order (Error 3) **Aktuell** in `dynamic-scripts.ts`: ``` queueDynamicScript(chunkA) → loadAndExecute(A) startet async queueDynamicScript(chunkB) → loadAndExecute(B) startet async ↓ beide await fetchContent() parallel → B's fetch completed first! → executeRaw(B) → dispatchEvent('load') → A completed → executeRaw(A) // REIHENFOLGE VERTAUSCHT → TDZ: 'K' aus A noch nicht initialisiert ``` **Spezifikation:** Browser verarbeitet Chunks in DOM-Insertion-Reihenfolge. Script-Evaluation ist synchron pro Chunk, nur das Netzwerk-Fetching ist parallel. --- ### Lösungsansatz #### Phase A: Inline Script Isolation **Änderungen in `parser.ts`:** 1. `start()` Methode: HTML-Template-Modus — Happy DOMs Script-Evaluation während `document.write()` unterdrücken 2. `executeScriptsInOrder()`: Alle Scripts (inline + external blocking) direkt via `ScriptLoader.onScriptTag()` ausführen, in Dokument-Reihenfolge 3. Inline-Scripts laufen NIEMALS in Happy DOMs Window — nur via `ExecutionRealm` **Änderungen in `dynamic-scripts.ts`:** 4. `patchConnectedToDocument()`: Inline-Scripts nicht mehr an Happy DOM delegieren 5. `disableEvaluation` bei inline scripts setzen **Änderungen in `execution-realm.ts`:** 6. Strict-Mode-Stripping vor Ausführung (verhindert dass `"use strict"` `this`-Binding zerstört) #### Phase B: Parser-Execution Integration **Änderungen in `parser.ts`:** 7. HTML in Chunks parsen, Script-Token isolieren 8. Bei inline/blocking script: Parser pausieren → Script ausführen → Parser fortsetzen 9. Parser-Resume Mechanismus #### Phase C: Dynamic Script Execution Queue **Änderungen in `dynamic-scripts.ts`:** 10. `chunkQueue` — FIFO-Queue für dynamische Scripts 11. `processQueue()` — sequentielle Verarbeitung (fetch parallel, execute serial) 12. `loadAndExecute()` → Queue-Entry statt eigenständiger Prozess --- ### Akzeptanzkriterien - [ ] Inline-Script `__OVERLAY__ = false` setzt Wert auf Proxy-Window → visible für External-Scripts - [ ] Inline-Script `window.GLOBAL_ENV = {...}` setzt Wert auf Proxy-Window - [ ] Alle bestehenden 1300 Tests passieren weiterhin (0 Regression) - [ ] Discord-lädt ohne `__OVERLAY__ is not defined` Fehler - [ ] Discord-lädt ohne `GLOBAL_ENV.RELEASE_CHANNEL` Fehler - [ ] Webpack-Chunks executieren in DOM-Insertion-Reihenfolge → kein `Cannot access 'K'` TDZ - [ ] Inline-Script-Side-Effekte feuern genau einmal (kein Double-Execution) - [ ] Deferred-Scripts laufen in Dokument-Reihenfolge - [ ] Neue Unit-Tests decken alle drei Phasen ab: - Inline-Script-Isolation (Proxy Window sichtbarkeit) - Chunk-Execution-Reihenfolge (Queue-Verhalten) - Kein Double-Execution - Deferred-Script-Ordering --- ### Betroffene Dateien | Datei | Phase | Änderung | |---|---|---| | `src/dom/parser.ts` | A+B | `start()` umbauen, `_disableScriptEvaluation()`, `executeScriptsInOrder()` | | `src/js/dynamic-scripts.ts` | A+C | `patchConnectedToDocument()` fix, `chunkQueue` + `processQueue()` | | `src/js/execution-realm.ts` | A | Strict-Mode-Stripping | | `src/js/script-loader.ts` | A+B | `executeInline()` ggf. anpassen | | `tests/unit/dynamic-scripts.test.ts` | C | Chunk-Ordering-Tests | | `tests/unit/execution-realm.test.ts` | A | Inline-Isolation-Tests | | `neu: tests/unit/script-pipeline.test.ts` | A+B+C | Integration-Tests für gesamte Pipeline | --- ### Risiken | Risiko | Phase | Mitigation | |---|---|---| | Happy DOM's `document.write()` erwartet inline execution | A | `_disableScriptEvaluation` als Flag; langfristig eigenen Parser | | Double-Execution durch Pre-Scan + document.write | A | Alle inline scripts via ExecutionRealm, nicht Happy DOM | | Reentrancy in Chunk-Queue (onload triggered neuen Chunk) | C | Queue appended hinten — kein Reentrancy-Problem | | Strict-Mode-Stripping entfernt legitime `"use strict"` | A | Nur top-level directive entfernen, nicht in Funktionen | | Performance durch serialisierte Chunk-Execution | C | Fetch bleibt parallel — nur execute serial (real browser genauso) | --- ### Testplan **Unit-Tests:** 1. `inline script sets window.__OVERLAY__` — nach Execution über Proxy lesbar 2. `inline script sets window.GLOBAL_ENV` — via evaluate() lesbar 3. `inline script runs exactly once` — Zähler vorher/nachher 4. `chunk queue executes in insertion order` — 3 chunks mit sequentiellen IDs 5. `chunk queue still loads concurrently` — fetch-Zeit messen (parallel) 6. `deferred scripts execute in document order` — Reihenfolge der Ausführung 7. `no double execution for pre-scanned scripts` — Parser + DynamicHandler **Integration-Tests:** 8. `full page load with inline config + external scripts` — simuliert Discord-Szenario 9. `dynamic webpack chunks maintain correct TDZ ordering` — zirkuläre Module simulieren 10. `regression: alle 1300+ bestehenden Tests` — voller Suite **E2E:** 11. `discord.com/app` — lädt ohne die 3 Engine-Fehler (manuell / CI)
Author
Owner

Spec-Conformer Script Execution Pipeline — Inline Isolation + Chunk Queue + Streaming Parser. Implementiert. Commit 72f91fe (feat) + 7f32e85 (fix) + 12e6619 (fix). Tests: 1423 pass.

Spec-Conformer Script Execution Pipeline — Inline Isolation + Chunk Queue + Streaming Parser. ✅ Implementiert. Commit 72f91fe (feat) + 7f32e85 (fix) + 12e6619 (fix). Tests: 1423 pass.
Artur closed this issue 2026-06-18 06:28:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#37
No description provided.