@salve-software/react-native-nitro-jsdom 1.1.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/android/CMakeLists.txt +6 -0
  2. package/cpp/HybridHtmlSandbox.cpp +14 -7
  3. package/cpp/HybridHtmlSandbox.hpp +1 -1
  4. package/cpp/lexbor/LexborDocument.cpp +65 -3
  5. package/cpp/lexbor/LexborDocument.hpp +17 -2
  6. package/cpp/quickjs/DOMBindings.cpp +14 -1
  7. package/cpp/quickjs/DOMBindingsInternal.cpp +67 -0
  8. package/cpp/quickjs/DOMBindingsInternal.hpp +39 -0
  9. package/cpp/quickjs/QuickJSRuntime.cpp +13 -0
  10. package/cpp/quickjs/QuickJSRuntime.hpp +29 -0
  11. package/cpp/quickjs/bindings/AbortBindings.cpp +14 -0
  12. package/cpp/quickjs/bindings/BlobBindings.cpp +28 -1
  13. package/cpp/quickjs/bindings/BlobBindings.hpp +3 -3
  14. package/cpp/quickjs/bindings/CSSOMBindings.cpp +96 -0
  15. package/cpp/quickjs/bindings/CSSOMBindings.hpp +3 -1
  16. package/cpp/quickjs/bindings/CustomElementsBindings.cpp +1 -1
  17. package/cpp/quickjs/bindings/DOMParserBindings.cpp +299 -0
  18. package/cpp/quickjs/bindings/DOMParserBindings.hpp +45 -0
  19. package/cpp/quickjs/bindings/DocumentBindings.cpp +174 -11
  20. package/cpp/quickjs/bindings/ElementBindings.cpp +516 -20
  21. package/cpp/quickjs/bindings/EventBindings.cpp +281 -5
  22. package/cpp/quickjs/bindings/EventBindings.hpp +3 -0
  23. package/cpp/quickjs/bindings/EventTargetBindings.cpp +75 -0
  24. package/cpp/quickjs/bindings/EventTargetBindings.hpp +24 -0
  25. package/cpp/quickjs/bindings/FetchBindings.cpp +43 -8
  26. package/cpp/quickjs/bindings/FormBindings.cpp +294 -0
  27. package/cpp/quickjs/bindings/FormBindings.hpp +9 -5
  28. package/cpp/quickjs/bindings/IntlBindings.cpp +352 -0
  29. package/cpp/quickjs/bindings/IntlBindings.hpp +29 -0
  30. package/cpp/quickjs/bindings/LayoutStubBindings.cpp +104 -0
  31. package/cpp/quickjs/bindings/LayoutStubBindings.hpp +28 -0
  32. package/cpp/quickjs/bindings/LiveCollectionBindings.cpp +45 -0
  33. package/cpp/quickjs/bindings/TimerBindings.cpp +79 -0
  34. package/cpp/quickjs/bindings/TreeWalkerBindings.cpp +306 -0
  35. package/cpp/quickjs/bindings/TreeWalkerBindings.hpp +28 -0
  36. package/cpp/quickjs/bindings/UrlBindings.cpp +43 -0
  37. package/cpp/quickjs/bindings/WindowBindings.cpp +295 -4
  38. package/cpp/quickjs/bindings/WindowBindings.hpp +3 -2
  39. package/cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp +241 -0
  40. package/cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp +30 -0
  41. package/lib/commonjs/classes/JSDOM/JSDOM.class.js +2 -1
  42. package/lib/commonjs/classes/JSDOM/JSDOM.class.js.map +1 -1
  43. package/lib/module/classes/JSDOM/JSDOM.class.js +2 -1
  44. package/lib/module/classes/JSDOM/JSDOM.class.js.map +1 -1
  45. package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts.map +1 -1
  46. package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts +5 -4
  47. package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts.map +1 -1
  48. package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts +1 -1
  49. package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts.map +1 -1
  50. package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.hpp +1 -1
  51. package/package.json +17 -2
  52. package/src/classes/JSDOM/JSDOM.class.ts +2 -1
  53. package/src/classes/JSDOM/types/IJSDOMOptions.ts +5 -4
  54. package/src/specs/HtmlSandbox.nitro.ts +1 -1
@@ -1,10 +1,11 @@
1
1
  #include "WindowBindings.hpp"
2
2
  #include "DOMExceptionBindings.hpp"
3
+ #include "EventBindings.hpp"
3
4
  #include "../DOMBindingsInternal.hpp"
4
5
  #include "../QuickJSRuntime.hpp"
6
+ #include <cstdlib>
5
7
  #include <cstring>
6
8
  #include <optional>
7
- #include <random>
8
9
  #include <string>
9
10
  #include <vector>
10
11
 
@@ -191,6 +192,23 @@ JSValue js_console_method(JSContext* ctx, JSValue, int argc, JSValue* argv, int
191
192
  return JS_UNDEFINED;
192
193
  }
193
194
 
195
+ JSValue js_window_reportError(JSContext* ctx, JSValue, int argc, JSValue* argv) {
196
+ JSValue error_value = argc >= 1 ? argv[0] : JS_UNDEFINED;
197
+ std::string message = "Uncaught";
198
+ if (argc >= 1) {
199
+ JSValue msg_prop = JS_GetPropertyStr(ctx, argv[0], "message");
200
+ if (!JS_IsException(msg_prop) && !JS_IsUndefined(msg_prop)) {
201
+ const char* s = JS_ToCString(ctx, msg_prop);
202
+ if (s) { message = s; JS_FreeCString(ctx, s); }
203
+ } else if (JS_IsException(msg_prop)) {
204
+ JS_FreeValue(ctx, JS_GetException(ctx));
205
+ }
206
+ JS_FreeValue(ctx, msg_prop);
207
+ }
208
+ EventBindings::dispatchErrorEvent(ctx, message, error_value);
209
+ return JS_UNDEFINED;
210
+ }
211
+
194
212
  // ── window.alert / confirm / prompt ───────────────────────────────────────────
195
213
 
196
214
  JSValue js_window_alert(JSContext* ctx, JSValue, int argc, JSValue* argv) {
@@ -336,6 +354,15 @@ const char* kLocationBootstrapScript = R"JS(
336
354
  globalThis.Location = Location;
337
355
  globalThis.location = new Location(globalThis.__initialHref);
338
356
  delete globalThis.__initialHref;
357
+
358
+ if (typeof document !== 'undefined') {
359
+ Object.defineProperty(document, 'location', {
360
+ get: function() { return location; },
361
+ set: function(v) { location.href = v; },
362
+ enumerable: true,
363
+ configurable: true,
364
+ });
365
+ }
339
366
  })();
340
367
  )JS";
341
368
 
@@ -346,12 +373,12 @@ JSValue js_native_random_bytes(JSContext* ctx, JSValue, int argc, JSValue* argv)
346
373
  if (argc >= 1) JS_ToInt32(ctx, &count, argv[0]);
347
374
  if (count < 0) count = 0;
348
375
 
349
- static thread_local std::mt19937 gen{std::random_device{}()};
350
- std::uniform_int_distribution<int> dist(0, 255);
376
+ std::vector<uint8_t> bytes(static_cast<size_t>(count));
377
+ if (count > 0) arc4random_buf(bytes.data(), bytes.size());
351
378
 
352
379
  JSValue arr = JS_NewArray(ctx);
353
380
  for (int32_t i = 0; i < count; i++) {
354
- JS_SetPropertyUint32(ctx, arr, static_cast<uint32_t>(i), JS_NewInt32(ctx, dist(gen)));
381
+ JS_SetPropertyUint32(ctx, arr, static_cast<uint32_t>(i), JS_NewInt32(ctx, bytes[static_cast<size_t>(i)]));
355
382
  }
356
383
  return arr;
357
384
  }
@@ -373,6 +400,67 @@ const char* kCryptoBootstrapScript = R"JS(
373
400
  for (var i = 0; i < bytes.length; i++) view[i] = bytes[i];
374
401
  return typedArray;
375
402
  };
403
+ globalThis.crypto.randomUUID = function() {
404
+ var bytes = __nativeRandomBytes(16);
405
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
406
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
407
+ var hex = [];
408
+ for (var i = 0; i < 16; i++) {
409
+ var h = bytes[i].toString(16);
410
+ hex.push(h.length === 1 ? '0' + h : h);
411
+ }
412
+ return hex.slice(0, 4).join('') + '-' + hex.slice(4, 6).join('') + '-' +
413
+ hex.slice(6, 8).join('') + '-' + hex.slice(8, 10).join('') + '-' + hex.slice(10, 16).join('');
414
+ };
415
+ })();
416
+ )JS";
417
+
418
+ // ── console ergonomics (group/table/assert/trace/count) ──────────────────────
419
+ // Layered in pure JS on top of the native log/warn/error/info/debug methods
420
+ // above rather than adding new native levels — none of these need anything
421
+ // the onConsole callback can't already receive as stringified args.
422
+
423
+ const char* kConsoleExtrasBootstrapScript = R"JS(
424
+ (function() {
425
+ var counts = Object.create(null);
426
+
427
+ console.group = function() {
428
+ console.log.apply(console, arguments);
429
+ };
430
+ console.groupCollapsed = console.group;
431
+ console.groupEnd = function() {};
432
+
433
+ console.trace = function() {
434
+ var args = Array.prototype.slice.call(arguments);
435
+ args.unshift('Trace:');
436
+ console.log.apply(console, args);
437
+ };
438
+
439
+ console.assert = function(condition) {
440
+ if (condition) return;
441
+ var args = Array.prototype.slice.call(arguments, 1);
442
+ args.unshift('Assertion failed:');
443
+ console.error.apply(console, args);
444
+ };
445
+
446
+ console.table = function(data) {
447
+ try {
448
+ console.log(JSON.stringify(data));
449
+ } catch (e) {
450
+ console.log(String(data));
451
+ }
452
+ };
453
+
454
+ console.count = function(label) {
455
+ label = label === undefined ? 'default' : String(label);
456
+ counts[label] = (counts[label] || 0) + 1;
457
+ console.log(label + ': ' + counts[label]);
458
+ };
459
+
460
+ console.countReset = function(label) {
461
+ label = label === undefined ? 'default' : String(label);
462
+ counts[label] = 0;
463
+ };
376
464
  })();
377
465
  )JS";
378
466
 
@@ -389,10 +477,184 @@ const char* kNavigatorBootstrapScript = R"JS(
389
477
  cookieEnabled: false,
390
478
  hardwareConcurrency: 1,
391
479
  };
480
+ navigator.sendBeacon = function(url, data) {
481
+ try {
482
+ fetch(url, { method: 'POST', body: data }).catch(function() {});
483
+ } catch (e) {
484
+ return false;
485
+ }
486
+ return true;
487
+ };
488
+
489
+ // jsdom itself doesn't implement the Clipboard API at all (no OS clipboard
490
+ // to back it, same reasoning as window.history/getSelection). This is an
491
+ // in-memory stand-in purely so 'copy discount code'-style embedded widgets
492
+ // that call navigator.clipboard.writeText() directly don't throw — reads
493
+ // back whatever was last written, nothing more.
494
+ var clipboardText = '';
495
+ navigator.clipboard = {
496
+ writeText: function(text) {
497
+ clipboardText = String(text);
498
+ return Promise.resolve();
499
+ },
500
+ readText: function() {
501
+ return Promise.resolve(clipboardText);
502
+ },
503
+ };
504
+
392
505
  globalThis.navigator = navigator;
393
506
  })();
394
507
  )JS";
395
508
 
509
+ // ── structuredClone ────────────────────────────────────────────────────────
510
+
511
+ const char* kStructuredCloneBootstrapScript = R"JS(
512
+ (function() {
513
+ globalThis.structuredClone = function(value) {
514
+ var seen = new Map();
515
+ function clone(v) {
516
+ if (v === null || typeof v !== 'object') {
517
+ if (typeof v === 'function' || typeof v === 'symbol') {
518
+ throw new DOMException(String(v) + ' could not be cloned.', 'DataCloneError');
519
+ }
520
+ return v;
521
+ }
522
+ if (seen.has(v)) return seen.get(v);
523
+ if (v instanceof Date) return new Date(v.getTime());
524
+ if (v instanceof RegExp) return new RegExp(v.source, v.flags);
525
+ if (Array.isArray(v)) {
526
+ var arr = [];
527
+ seen.set(v, arr);
528
+ for (var i = 0; i < v.length; i++) arr.push(clone(v[i]));
529
+ return arr;
530
+ }
531
+ if (v instanceof Map) {
532
+ var m = new Map();
533
+ seen.set(v, m);
534
+ v.forEach(function(val, key) { m.set(clone(key), clone(val)); });
535
+ return m;
536
+ }
537
+ if (v instanceof Set) {
538
+ var s = new Set();
539
+ seen.set(v, s);
540
+ v.forEach(function(val) { s.add(clone(val)); });
541
+ return s;
542
+ }
543
+ if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && ArrayBuffer.isView(v)) {
544
+ return new v.constructor(v);
545
+ }
546
+ if (typeof ArrayBuffer !== 'undefined' && v instanceof ArrayBuffer) {
547
+ return v.slice(0);
548
+ }
549
+ var proto = Object.getPrototypeOf(v);
550
+ if (proto !== Object.prototype && proto !== null) {
551
+ throw new DOMException('The object could not be cloned.', 'DataCloneError');
552
+ }
553
+ var out = {};
554
+ seen.set(v, out);
555
+ for (var key in v) {
556
+ if (Object.prototype.hasOwnProperty.call(v, key)) out[key] = clone(v[key]);
557
+ }
558
+ return out;
559
+ }
560
+ return clone(value);
561
+ };
562
+ })();
563
+ )JS";
564
+
565
+ // ── window.history ────────────────────────────────────────────────────────
566
+ // No real page navigation exists in this sandbox, so History tracks its own
567
+ // in-memory entry stack rather than session history. pushState/replaceState
568
+ // never fire popstate (per spec); go/back/forward do, since those are the
569
+ // events CMS-widget routers actually listen for. `title` is accepted but
570
+ // ignored (same as jsdom — nothing renders a document title). State is
571
+ // structuredClone()'d on write (per spec — matches pushState/replaceState's
572
+ // documented "serializable" semantics), so callers mutating their object
573
+ // after the call can't leak into a stored entry. `go()`'s delta is truncated
574
+ // to an integer so a fractional/NaN input can't produce a non-integer
575
+ // `_index`.
576
+
577
+ const char* kHistoryBootstrapScript = R"JS(
578
+ (function() {
579
+ function History() {
580
+ this._entries = [null];
581
+ this._index = 0;
582
+ }
583
+ Object.defineProperty(History.prototype, 'length', {
584
+ get: function() { return this._entries.length; },
585
+ enumerable: true,
586
+ });
587
+ Object.defineProperty(History.prototype, 'state', {
588
+ get: function() { return this._entries[this._index]; },
589
+ enumerable: true,
590
+ });
591
+ function cloneState(state) {
592
+ return state === undefined ? null : structuredClone(state);
593
+ }
594
+ History.prototype.pushState = function(state, _title, url) {
595
+ this._entries = this._entries.slice(0, this._index + 1);
596
+ this._entries.push(cloneState(state));
597
+ this._index++;
598
+ if (url !== undefined && url !== null) location.href = url;
599
+ };
600
+ History.prototype.replaceState = function(state, _title, url) {
601
+ this._entries[this._index] = cloneState(state);
602
+ if (url !== undefined && url !== null) location.href = url;
603
+ };
604
+ History.prototype.go = function(delta) {
605
+ delta = delta === undefined ? 0 : Math.trunc(Number(delta) || 0);
606
+ if (delta === 0) return;
607
+ var next = this._index + delta;
608
+ if (next < 0 || next > this._entries.length - 1) return;
609
+ this._index = next;
610
+ var ev = new Event('popstate');
611
+ ev.state = this._entries[this._index];
612
+ dispatchEvent(ev);
613
+ };
614
+ History.prototype.back = function() { this.go(-1); };
615
+ History.prototype.forward = function() { this.go(1); };
616
+
617
+ globalThis.History = History;
618
+ globalThis.history = new History();
619
+ })();
620
+ )JS";
621
+
622
+ // ── window.getSelection ──────────────────────────────────────────────────
623
+ // No layout engine backs this sandbox, so there is nothing to select — this
624
+ // mirrors matchMedia's stance: an always-empty Selection rather than a
625
+ // missing global, so defensive `window.getSelection().toString()` checks in
626
+ // third-party scripts don't crash the sandbox.
627
+
628
+ const char* kSelectionBootstrapScript = R"JS(
629
+ (function() {
630
+ function Selection() {}
631
+ Object.defineProperty(Selection.prototype, 'rangeCount', { get: function() { return 0; }, enumerable: true });
632
+ Object.defineProperty(Selection.prototype, 'isCollapsed', { get: function() { return true; }, enumerable: true });
633
+ Object.defineProperty(Selection.prototype, 'anchorNode', { get: function() { return null; }, enumerable: true });
634
+ Object.defineProperty(Selection.prototype, 'anchorOffset', { get: function() { return 0; }, enumerable: true });
635
+ Object.defineProperty(Selection.prototype, 'focusNode', { get: function() { return null; }, enumerable: true });
636
+ Object.defineProperty(Selection.prototype, 'focusOffset', { get: function() { return 0; }, enumerable: true });
637
+ Object.defineProperty(Selection.prototype, 'type', { get: function() { return 'None'; }, enumerable: true });
638
+ Selection.prototype.toString = function() { return ''; };
639
+ Selection.prototype.removeAllRanges = function() {};
640
+ Selection.prototype.collapse = function() {};
641
+ Selection.prototype.collapseToStart = function() {};
642
+ Selection.prototype.collapseToEnd = function() {};
643
+ Selection.prototype.selectAllChildren = function() {};
644
+ Selection.prototype.addRange = function() {};
645
+ Selection.prototype.containsNode = function() { return false; };
646
+ Selection.prototype.getRangeAt = function(index) {
647
+ throw new DOMException(
648
+ "Failed to execute 'getRangeAt' on 'Selection': " + index + ' is not a valid index.', 'IndexSizeError');
649
+ };
650
+ Selection.prototype.empty = Selection.prototype.removeAllRanges;
651
+
652
+ var selectionInstance = new Selection();
653
+ globalThis.Selection = Selection;
654
+ globalThis.getSelection = function() { return selectionInstance; };
655
+ })();
656
+ )JS";
657
+
396
658
  // ── matchMedia ─────────────────────────────────────────────────────────────
397
659
  // No layout engine backs this sandbox, so every query reports "no match" —
398
660
  // mirrors jsdom's own stance that layout-dependent APIs return inert defaults
@@ -435,6 +697,7 @@ void WindowBindings::install(JSContext* ctx) {
435
697
  JS_SetPropertyStr(ctx, global, "alert", JS_NewCFunction(ctx, js_window_alert, "alert", 1));
436
698
  JS_SetPropertyStr(ctx, global, "confirm", JS_NewCFunction(ctx, js_window_confirm, "confirm", 1));
437
699
  JS_SetPropertyStr(ctx, global, "prompt", JS_NewCFunction(ctx, js_window_prompt, "prompt", 2));
700
+ JS_SetPropertyStr(ctx, global, "reportError", JS_NewCFunction(ctx, js_window_reportError, "reportError", 1));
438
701
  JS_SetPropertyStr(ctx, global, "atob", JS_NewCFunction(ctx, js_window_atob, "atob", 1));
439
702
  JS_SetPropertyStr(ctx, global, "btoa", JS_NewCFunction(ctx, js_window_btoa, "btoa", 1));
440
703
 
@@ -456,6 +719,27 @@ void WindowBindings::install(JSContext* ctx) {
456
719
  }
457
720
  JS_FreeValue(ctx, location_result);
458
721
 
722
+ JSValue history_result = JS_Eval(ctx, kHistoryBootstrapScript, strlen(kHistoryBootstrapScript),
723
+ "<history-bootstrap>", JS_EVAL_TYPE_GLOBAL);
724
+ if (JS_IsException(history_result)) {
725
+ JS_FreeValue(ctx, JS_GetException(ctx));
726
+ }
727
+ JS_FreeValue(ctx, history_result);
728
+
729
+ JSValue selection_result = JS_Eval(ctx, kSelectionBootstrapScript, strlen(kSelectionBootstrapScript),
730
+ "<selection-bootstrap>", JS_EVAL_TYPE_GLOBAL);
731
+ if (JS_IsException(selection_result)) {
732
+ JS_FreeValue(ctx, JS_GetException(ctx));
733
+ }
734
+ JS_FreeValue(ctx, selection_result);
735
+
736
+ JSValue console_extras_result = JS_Eval(ctx, kConsoleExtrasBootstrapScript, strlen(kConsoleExtrasBootstrapScript),
737
+ "<console-extras-bootstrap>", JS_EVAL_TYPE_GLOBAL);
738
+ if (JS_IsException(console_extras_result)) {
739
+ JS_FreeValue(ctx, JS_GetException(ctx));
740
+ }
741
+ JS_FreeValue(ctx, console_extras_result);
742
+
459
743
  JSValue crypto_result = JS_Eval(ctx, kCryptoBootstrapScript, strlen(kCryptoBootstrapScript),
460
744
  "<crypto-bootstrap>", JS_EVAL_TYPE_GLOBAL);
461
745
  if (JS_IsException(crypto_result)) {
@@ -476,6 +760,13 @@ void WindowBindings::install(JSContext* ctx) {
476
760
  JS_FreeValue(ctx, JS_GetException(ctx));
477
761
  }
478
762
  JS_FreeValue(ctx, match_media_result);
763
+
764
+ JSValue structured_clone_result = JS_Eval(ctx, kStructuredCloneBootstrapScript, strlen(kStructuredCloneBootstrapScript),
765
+ "<structured-clone-bootstrap>", JS_EVAL_TYPE_GLOBAL);
766
+ if (JS_IsException(structured_clone_result)) {
767
+ JS_FreeValue(ctx, JS_GetException(ctx));
768
+ }
769
+ JS_FreeValue(ctx, structured_clone_result);
479
770
  }
480
771
 
481
772
  } // namespace margelo::nitro::nitrojsdom
@@ -1,8 +1,9 @@
1
1
  #pragma once
2
2
 
3
3
  // window.alert/confirm/prompt (bridged to onAlert/onConfirm/onPrompt native
4
- // callbacks), console.log/warn/error/info/debug, and the window.location
5
- // bootstrap script.
4
+ // callbacks), console.log/warn/error/info/debug, and the window.location,
5
+ // window.history, window.getSelection, crypto, navigator, matchMedia, and
6
+ // structuredClone bootstrap scripts.
6
7
 
7
8
  #include "quickjs.h"
8
9
 
@@ -0,0 +1,241 @@
1
+ #include "WindowNamedPropertiesBindings.hpp"
2
+ #include <cstring>
3
+
4
+ namespace margelo::nitro::nitrojsdom {
5
+
6
+ namespace {
7
+
8
+ const char* kWindowNamedPropertiesBootstrapScript = R"JS(
9
+ (function() {
10
+ var NAME_ATTR_TAGS = { embed: 1, form: 1, img: 1, object: 1, iframe: 1, frame: 1 };
11
+
12
+ // registry: key -> element[] currently claiming it. owned: key -> true once
13
+ // this module has written globalThis[key] (so it knows it's safe to delete
14
+ // or reassign later without clobbering something else's global).
15
+ var registry = Object.create(null);
16
+ var owned = Object.create(null);
17
+
18
+ function isConnected(el) {
19
+ var docEl = document.documentElement;
20
+ var n = el;
21
+ while (n) {
22
+ if (n === docEl) return true;
23
+ n = n.parentNode;
24
+ }
25
+ return false;
26
+ }
27
+
28
+ function refreshKey(key) {
29
+ var els = registry[key];
30
+ if (!els || els.length === 0) {
31
+ if (owned[key]) { delete globalThis[key]; delete owned[key]; }
32
+ return;
33
+ }
34
+ if (!owned[key] && (key in globalThis)) return; // don't clobber a real global
35
+ globalThis[key] = els.length === 1 ? els[0] : els.slice();
36
+ owned[key] = true;
37
+ }
38
+
39
+ function addToRegistry(key, el) {
40
+ if (!key) return;
41
+ var els = registry[key] || (registry[key] = []);
42
+ if (els.indexOf(el) === -1) els.push(el);
43
+ refreshKey(key);
44
+ }
45
+
46
+ function removeFromRegistry(key, el) {
47
+ if (!key) return;
48
+ var els = registry[key];
49
+ if (!els) return;
50
+ var idx = els.indexOf(el);
51
+ if (idx !== -1) els.splice(idx, 1);
52
+ refreshKey(key);
53
+ }
54
+
55
+ function keysFor(el) {
56
+ var keys = [];
57
+ var id = el.getAttribute ? el.getAttribute('id') : null;
58
+ if (id) keys.push(id);
59
+ var tag = el.tagName ? el.tagName.toLowerCase() : '';
60
+ if (NAME_ATTR_TAGS[tag]) {
61
+ var name = el.getAttribute('name');
62
+ if (name) keys.push(name);
63
+ }
64
+ return keys;
65
+ }
66
+
67
+ function registerIfConnected(el) {
68
+ if (!el || el.nodeType !== 1 || !isConnected(el)) return;
69
+ keysFor(el).forEach(function(k) { addToRegistry(k, el); });
70
+ }
71
+
72
+ function unregister(el) {
73
+ if (!el || el.nodeType !== 1) return;
74
+ keysFor(el).forEach(function(k) { removeFromRegistry(k, el); });
75
+ }
76
+
77
+ function registerSubtree(root) {
78
+ if (!root) return;
79
+ if (root.nodeType === 1) registerIfConnected(root);
80
+ if (typeof root.querySelectorAll !== 'function') return;
81
+ var found = root.querySelectorAll('*');
82
+ for (var i = 0; i < found.length; i++) registerIfConnected(found[i]);
83
+ }
84
+
85
+ function unregisterSubtree(root) {
86
+ if (!root) return;
87
+ if (root.nodeType === 1) unregister(root);
88
+ if (typeof root.querySelectorAll !== 'function') return;
89
+ var found = root.querySelectorAll('*');
90
+ for (var i = 0; i < found.length; i++) unregister(found[i]);
91
+ }
92
+
93
+ function collectSubtreeKeys(root) {
94
+ var pending = [];
95
+ function collect(el) {
96
+ if (!el || el.nodeType !== 1) return;
97
+ keysFor(el).forEach(function(k) { pending.push({ key: k, el: el }); });
98
+ }
99
+ if (!root) return pending;
100
+ collect(root);
101
+ if (typeof root.querySelectorAll === 'function') {
102
+ var found = root.querySelectorAll('*');
103
+ for (var i = 0; i < found.length; i++) collect(found[i]);
104
+ }
105
+ return pending;
106
+ }
107
+
108
+ function commitUnregister(pending) {
109
+ pending.forEach(function(entry) { removeFromRegistry(entry.key, entry.el); });
110
+ }
111
+
112
+ // ── Initial population: elements already present in the parsed document ──
113
+ if (document.documentElement) registerSubtree(document.documentElement);
114
+
115
+ // ── appendChild / removeChild ─────────────────────────────────────────
116
+ var DOCUMENT_FRAGMENT_NODE = 11;
117
+
118
+ var origAppendChild = Node.prototype.appendChild;
119
+ Node.prototype.appendChild = function(child) {
120
+ var moved = (child && child.nodeType === DOCUMENT_FRAGMENT_NODE)
121
+ ? Array.prototype.slice.call(child.childNodes)
122
+ : [child];
123
+ var result = origAppendChild.call(this, child);
124
+ moved.forEach(registerSubtree);
125
+ return result;
126
+ };
127
+
128
+ var origRemoveChild = Node.prototype.removeChild;
129
+ Node.prototype.removeChild = function(child) {
130
+ var pending = collectSubtreeKeys(child);
131
+ var willRemove = child && child.parentNode === this;
132
+ var result = origRemoveChild.call(this, child);
133
+ if (willRemove) commitUnregister(pending);
134
+ return result;
135
+ };
136
+
137
+ var origRemove = Element.prototype.remove;
138
+ Element.prototype.remove = function() {
139
+ var pending = collectSubtreeKeys(this);
140
+ var hadParent = this.parentNode !== null;
141
+ var result = origRemove.call(this);
142
+ if (hadParent) commitUnregister(pending);
143
+ return result;
144
+ };
145
+
146
+ // ── innerHTML ────────────────────────────────────────────────────────
147
+ var innerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
148
+ if (innerHTMLDesc && innerHTMLDesc.set) {
149
+ var origInnerHTMLSet = innerHTMLDesc.set;
150
+ Object.defineProperty(Element.prototype, 'innerHTML', {
151
+ get: innerHTMLDesc.get,
152
+ set: function(html) {
153
+ unregisterSubtree(this); // walk the OLD content before it's replaced
154
+ origInnerHTMLSet.call(this, html);
155
+ registerSubtree(this); // register the NEW content
156
+ },
157
+ enumerable: innerHTMLDesc.enumerable,
158
+ configurable: true,
159
+ });
160
+ }
161
+
162
+ var textContentDesc = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent');
163
+ if (textContentDesc && textContentDesc.set) {
164
+ var origTextContentSet = textContentDesc.set;
165
+ Object.defineProperty(Node.prototype, 'textContent', {
166
+ get: textContentDesc.get,
167
+ set: function(value) {
168
+ unregisterSubtree(this);
169
+ origTextContentSet.call(this, value);
170
+ },
171
+ enumerable: textContentDesc.enumerable,
172
+ configurable: true,
173
+ });
174
+ }
175
+
176
+ // ── insertAdjacentHTML ──────────────────────────────────────────────
177
+ var origInsertAdjacentHTML = Element.prototype.insertAdjacentHTML;
178
+ Element.prototype.insertAdjacentHTML = function(position, html) {
179
+ origInsertAdjacentHTML.call(this, position, html);
180
+ var pos = String(position).toLowerCase();
181
+ registerSubtree((pos === 'beforebegin' || pos === 'afterend') ? (this.parentNode || this) : this);
182
+ };
183
+
184
+ // ── element.id (native accessor property — bypasses setAttribute) ──
185
+ var idDesc = Object.getOwnPropertyDescriptor(Element.prototype, 'id');
186
+ if (idDesc && idDesc.set) {
187
+ var origIdSet = idDesc.set;
188
+ Object.defineProperty(Element.prototype, 'id', {
189
+ get: idDesc.get,
190
+ set: function(value) {
191
+ var oldValue = this.getAttribute('id');
192
+ origIdSet.call(this, value);
193
+ if (oldValue) removeFromRegistry(oldValue, this);
194
+ registerIfConnected(this);
195
+ },
196
+ enumerable: idDesc.enumerable,
197
+ configurable: true,
198
+ });
199
+ }
200
+
201
+ // ── setAttribute / removeAttribute (id/name changes) ───────────────
202
+ function isTrackedAttr(el, name) {
203
+ var lower = String(name).toLowerCase();
204
+ if (lower === 'id') return true;
205
+ var tag = el.tagName ? el.tagName.toLowerCase() : '';
206
+ return lower === 'name' && !!NAME_ATTR_TAGS[tag];
207
+ }
208
+
209
+ var origSetAttribute = Element.prototype.setAttribute;
210
+ Element.prototype.setAttribute = function(name, value) {
211
+ var tracked = isTrackedAttr(this, name);
212
+ var oldValue = tracked ? this.getAttribute(name) : null;
213
+ origSetAttribute.call(this, name, value);
214
+ if (tracked) {
215
+ if (oldValue) removeFromRegistry(oldValue, this);
216
+ registerIfConnected(this);
217
+ }
218
+ };
219
+
220
+ var origRemoveAttribute = Element.prototype.removeAttribute;
221
+ Element.prototype.removeAttribute = function(name) {
222
+ var tracked = isTrackedAttr(this, name);
223
+ var oldValue = tracked ? this.getAttribute(name) : null;
224
+ origRemoveAttribute.call(this, name);
225
+ if (tracked && oldValue) removeFromRegistry(oldValue, this);
226
+ };
227
+ })();
228
+ )JS";
229
+
230
+ } // namespace
231
+
232
+ void WindowNamedPropertiesBindings::install(JSContext* ctx) {
233
+ JSValue result = JS_Eval(ctx, kWindowNamedPropertiesBootstrapScript, strlen(kWindowNamedPropertiesBootstrapScript),
234
+ "<window-named-properties-bootstrap>", JS_EVAL_TYPE_GLOBAL);
235
+ if (JS_IsException(result)) {
236
+ JS_FreeValue(ctx, JS_GetException(ctx));
237
+ }
238
+ JS_FreeValue(ctx, result);
239
+ }
240
+
241
+ } // namespace margelo::nitro::nitrojsdom
@@ -0,0 +1,30 @@
1
+ #pragma once
2
+
3
+ #include "quickjs.h"
4
+
5
+ namespace margelo::nitro::nitrojsdom {
6
+
7
+ // Implements "named access on the Window object"
8
+ // (https://html.spec.whatwg.org/#named-access-on-the-window-object): an
9
+ // element with an `id`, or a `name` attribute on `embed`/`form`/`img`/
10
+ // `object`/`iframe`/`frame`, becomes reachable as a bare global while
11
+ // connected to the primary document.
12
+ //
13
+ // Pure JS, monkey-patching appendChild/removeChild/remove/innerHTML/
14
+ // textContent/insertAdjacentHTML/setAttribute/removeAttribute/the `id`
15
+ // accessor — no new native bindings, no engine-level Proxy (QuickJS's real
16
+ // global object can't be swapped for one), so exposure is push-based.
17
+ //
18
+ // Duplicate ids/names resolve to a plain array, not a live `HTMLCollection`
19
+ // (same trade-off as `form.elements`/`element.labels`). insertBefore/before/
20
+ // after/replaceWith/append/prepend/insertAdjacentElement are not hooked
21
+ // (same scope CustomElementsBindings' connectivity tracking accepted).
22
+ // Never overwrites a key already present on `globalThis`.
23
+ //
24
+ // Must run last in DOMBindings::install (after localStorage/sessionStorage
25
+ // exist, so it never claims those keys).
26
+ struct WindowNamedPropertiesBindings {
27
+ static void install(JSContext* ctx);
28
+ };
29
+
30
+ } // namespace margelo::nitro::nitrojsdom
@@ -47,7 +47,8 @@ class JSDOM {
47
47
  sandbox.setConsoleCallback(null);
48
48
  }
49
49
  if (options?.onAlert || options?.onConfirm || options?.onPrompt) {
50
- sandbox.setDialogCallbacks(options.onAlert ?? null, options.onConfirm ?? null, options.onPrompt ?? null);
50
+ const onConfirm = options.onConfirm;
51
+ sandbox.setDialogCallbacks(options.onAlert ?? null, onConfirm ? async message => await onConfirm(message) : null, options.onPrompt ?? null);
51
52
  }
52
53
  if (options?.onFetch) {
53
54
  const onFetch = options.onFetch;
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNativeNitroModules","require","safeParseHeaders","headersJson","parsed","JSON","parse","JSDOM","_disposed","constructor","sandbox","create","html","options","NitroModules","createHybridObject","initialize","runScripts","url","pretendToBeVisual","onConsole","cb","setConsoleCallback","level","args","onAlert","onConfirm","onPrompt","setDialogCallbacks","onFetch","setFetchCallback","method","body","headers","then","res","stringify","ok","status","statusText","catch","err","error","Error","message","String","evaluate","script","Promise","reject","serialize","dispose","exports"],"sourceRoot":"../../../../src","sources":["classes/JSDOM/JSDOM.class.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_reactNativeNitroModules","require","safeParseHeaders","headersJson","parsed","JSON","parse","JSDOM","_disposed","constructor","sandbox","create","html","options","NitroModules","createHybridObject","initialize","runScripts","url","pretendToBeVisual","onConsole","cb","setConsoleCallback","level","args","onAlert","onConfirm","onPrompt","setDialogCallbacks","message","onFetch","setFetchCallback","method","body","headers","then","res","stringify","ok","status","statusText","catch","err","error","Error","String","evaluate","script","Promise","reject","serialize","dispose","exports"],"sourceRoot":"../../../../src","sources":["classes/JSDOM/JSDOM.class.ts"],"mappings":"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","ignoreList":[]}