xbintsc 0.5.36 → 0.5.38

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 (124) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/codegen/generator/state.js +1 -0
  3. package/dist/src/codegen/generator/state.js.map +1 -1
  4. package/dist/src/codegen/generator/tables.js +1 -0
  5. package/dist/src/codegen/generator/tables.js.map +1 -1
  6. package/dist/src/driver/bundler/deferred.d.ts +63 -0
  7. package/dist/src/driver/bundler/deferred.js +279 -0
  8. package/dist/src/driver/bundler/deferred.js.map +1 -0
  9. package/dist/src/driver/bundler/merge.js +214 -2
  10. package/dist/src/driver/bundler/merge.js.map +1 -1
  11. package/dist/src/driver/runtime-objects.d.ts +1 -1
  12. package/dist/src/driver/runtime-objects.js +1 -0
  13. package/dist/src/driver/runtime-objects.js.map +1 -1
  14. package/dist/src/driver/version.d.ts +1 -1
  15. package/dist/src/driver/version.js +1 -1
  16. package/dist/src/extensions/gui/html.js +89 -13
  17. package/dist/src/extensions/gui/html.js.map +1 -1
  18. package/dist/src/extensions/gui/index.js +13 -0
  19. package/dist/src/extensions/gui/index.js.map +1 -1
  20. package/dist/src/extensions/gui/three.d.ts +4 -3
  21. package/dist/src/extensions/gui/three.js +4 -3
  22. package/dist/src/extensions/gui/three.js.map +1 -1
  23. package/dist/tests/e2e/gui-browser.test.js +65 -0
  24. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  25. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  26. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  27. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  28. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  29. package/dist/tests/e2e/gui-dom.test.js +215 -0
  30. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  31. package/dist/tests/e2e/gui-events.test.js +112 -0
  32. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  33. package/dist/tests/e2e/gui-script-import-order.test.d.ts +10 -0
  34. package/dist/tests/e2e/gui-script-import-order.test.js +161 -0
  35. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -0
  36. package/dist/tests/e2e/gui-webgl2-shadow.test.d.ts +10 -0
  37. package/dist/tests/e2e/gui-webgl2-shadow.test.js +108 -0
  38. package/dist/tests/e2e/gui-webgl2-shadow.test.js.map +1 -0
  39. package/dist/tests/e2e/url.test.d.ts +6 -0
  40. package/dist/tests/e2e/url.test.js +114 -0
  41. package/dist/tests/e2e/url.test.js.map +1 -0
  42. package/dist/tests/extensions/gui.test.js +22 -10
  43. package/dist/tests/extensions/gui.test.js.map +1 -1
  44. package/doc/gui/api.md +38 -9
  45. package/doc/gui/canvas.md +49 -18
  46. package/doc/gui/dom.md +51 -4
  47. package/doc/gui/webgl.md +20 -10
  48. package/doc/gui-scripts.md +19 -19
  49. package/doc/gui.md +10 -8
  50. package/doc/implemented/fetch.md +5 -0
  51. package/doc/implemented/modules.md +13 -0
  52. package/doc/implemented/stdlib.md +17 -0
  53. package/doc/unimplemented/deviations.md +1 -0
  54. package/doc/zh-CN/gui/api.md +33 -4
  55. package/doc/zh-CN/gui/canvas.md +44 -19
  56. package/doc/zh-CN/gui/dom.md +45 -3
  57. package/doc/zh-CN/gui/webgl.md +3 -3
  58. package/doc/zh-CN/gui-scripts.md +14 -13
  59. package/doc/zh-CN/gui.md +7 -6
  60. package/doc/zh-CN/implemented/fetch.md +4 -0
  61. package/doc/zh-CN/implemented/modules.md +10 -0
  62. package/doc/zh-CN/implemented/stdlib.md +15 -0
  63. package/doc/zh-CN/unimplemented/deviations.md +1 -0
  64. package/package.json +7 -7
  65. package/runtime/ext_gui/browser.cpp +34 -49
  66. package/runtime/ext_gui/browser_abort.cpp +249 -0
  67. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  68. package/runtime/ext_gui/browser_media.cpp +176 -0
  69. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  70. package/runtime/ext_gui/canvas.cpp +88 -30
  71. package/runtime/ext_gui/canvas.h +74 -9
  72. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  73. package/runtime/ext_gui/canvas_internal.h +8 -5
  74. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  75. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  76. package/runtime/ext_gui/canvas_style.cpp +4 -2
  77. package/runtime/ext_gui/dom.h +4 -0
  78. package/runtime/ext_gui/dom_api.cpp +1 -0
  79. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  80. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  81. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  82. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  83. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  84. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  85. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  86. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  87. package/runtime/ext_gui/dom_api_internal.h +55 -2
  88. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  89. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  90. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  91. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  92. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  93. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  94. package/runtime/ext_gui/dom_api_webgl.cpp +10 -0
  95. package/runtime/ext_gui/dom_api_webgl2.cpp +33 -1
  96. package/runtime/ext_gui/gui.cpp +59 -5
  97. package/runtime/ext_gui/gui_engine.h +49 -0
  98. package/runtime/ext_gui/gui_events.cpp +80 -9
  99. package/runtime/ext_gui/listener_options.h +43 -0
  100. package/runtime/ext_gui/paint.cpp +28 -0
  101. package/runtime/ext_gui/paint.h +4 -0
  102. package/runtime/ext_gui/script.cpp +31 -0
  103. package/runtime/ext_gui/webgl_glsl.h +3 -0
  104. package/runtime/ext_gui/webgl_glsl_unroll.cpp +283 -0
  105. package/runtime/ext_gui/webgl_msl.cpp +4 -0
  106. package/runtime/ext_gui/webgl_shader.cpp +12 -6
  107. package/runtime/ext_gui/window.cpp +14 -65
  108. package/runtime/ext_gui/window_events.cpp +125 -0
  109. package/runtime/rt.h +29 -0
  110. package/runtime/rt_internal.h +4 -0
  111. package/runtime/xt_builtins.c +0 -21
  112. package/runtime/xt_containers/access.inc +94 -2
  113. package/runtime/xt_containers/objects.inc +29 -31
  114. package/runtime/xt_fetch/async.inc +60 -0
  115. package/runtime/xt_url_object.c +463 -0
  116. package/src/codegen/generator/state.ts +1 -0
  117. package/src/codegen/generator/tables.ts +1 -0
  118. package/src/driver/bundler/deferred.ts +351 -0
  119. package/src/driver/bundler/merge.ts +219 -2
  120. package/src/driver/runtime-objects.ts +1 -0
  121. package/src/driver/version.ts +1 -1
  122. package/src/extensions/gui/html.ts +91 -15
  123. package/src/extensions/gui/index.ts +13 -0
  124. package/src/extensions/gui/three.ts +4 -3
@@ -8,6 +8,7 @@
8
8
  | --- | --- |
9
9
  | 正则按字节匹配 | 引擎按 UTF-8 字节匹配:多字节字面量仍可匹配,但 `.` 与字符类每次只消费一个字节(`\d`/`\w` 仍是 ASCII)。`exec` / `search` / 替换回调返回的偏移会转换为 UTF-16 码元 |
10
10
  | 全局正则 `lastIndex` | `/g`、`/y` 正则的 `test` / `exec` 不推进也不读取调用方设置的 `lastIndex` |
11
+ | `URL` 解析器 | 只读 WHATWG 子集:未实现路径百分号编码、IDNA、setter 与 `searchParams` 实时联动;无协议引用按工作目录解析为 `file:` URL |
11
12
  | `String.normalize` | 未实现 |
12
13
  | 内置方法一等公民 | 以未绑定方法值实现(见[函数](./functions.md));`fn.toString()` 返回占位字符串而非源码文本 |
13
14
  | `for...in` | 枚举对象 / 数组 / 字符串的自身键;不含原型链属性 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xbintsc",
3
- "version": "0.5.36",
3
+ "version": "0.5.38",
4
4
  "description": "xbintsc - a TypeScript binary compiler (TypeScript -> LLVM IR -> native binary)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -72,12 +72,12 @@
72
72
  "vitest": "^2.1.8"
73
73
  },
74
74
  "optionalDependencies": {
75
- "@xbintsc/linux-x64": "0.5.36",
76
- "@xbintsc/linux-arm64": "0.5.36",
77
- "@xbintsc/darwin-x64": "0.5.36",
78
- "@xbintsc/darwin-arm64": "0.5.36",
79
- "@xbintsc/win32-x64": "0.5.36",
80
- "@xbintsc/win32-arm64": "0.5.36"
75
+ "@xbintsc/linux-x64": "0.5.38",
76
+ "@xbintsc/linux-arm64": "0.5.38",
77
+ "@xbintsc/darwin-x64": "0.5.38",
78
+ "@xbintsc/darwin-arm64": "0.5.38",
79
+ "@xbintsc/win32-x64": "0.5.38",
80
+ "@xbintsc/win32-arm64": "0.5.38"
81
81
  },
82
82
  "engines": {
83
83
  "node": ">=22"
@@ -29,7 +29,6 @@ namespace {
29
29
  xt_value g_navigator = XT_UNDEFINED;
30
30
  xt_value g_local_storage = XT_UNDEFINED;
31
31
  xt_value g_location = XT_UNDEFINED;
32
- xt_value g_media_list = XT_UNDEFINED;
33
32
  xt_value g_document_global = XT_UNDEFINED;
34
33
  /** Type name -> the constructor value handed out for it (built on first use). */
35
34
  std::unordered_map<std::string, xt_value> g_types;
@@ -156,35 +155,28 @@ xt_value win_location_get(xt_value self, xt_value env, int32_t argc, xt_value *a
156
155
  return g_location;
157
156
  }
158
157
 
159
- xt_value media_list_build(const std::string &query, bool matches);
160
-
161
- /** `matchMedia(query)` -> a MediaQueryList-shaped object evaluated against the
162
- * current window's viewport, using the same evaluator as `@media` rules. The
163
- * list is a snapshot (there is no live change event), which matches the
164
- * engine's restyle-on-resize model. */
158
+ /** `matchMedia(query)` -> a live `MediaQueryList` evaluated against the current
159
+ * viewport with the same evaluator as `@media` rules. The list is cached per
160
+ * window and query, and `change` listeners fire when a resize flips it. */
165
161
  xt_value win_match_media(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
166
162
  (void)env;
167
163
  std::string query;
168
164
  if (argc >= 1) query = cstr(xt_arg(argc, argv, 0));
169
165
  XtGuiWindow *win = xt_gui_window_from_this(self);
170
- if (win == NULL) {
171
- xt_value handle = xt_gui_ensure_window();
172
- win = xt_gui_window_from_this(handle);
173
- }
174
- float width = 0.0f;
175
- float height = 0.0f;
176
- if (win != NULL) xt_gui_window_viewport(win, &width, &height);
177
- return media_list_build(query, xtgui::xt_css_media_matches(query, width, height));
166
+ if (win == NULL) win = xt_gui_window_from_this(xt_gui_ensure_window());
167
+ if (win == NULL) return xt_object_new();
168
+ return xt_browser_media_list(win->object, query);
178
169
  }
179
170
 
180
- /** `getComputedStyle(el)`: the engine exposes the element's style handle, which
181
- * answers `getPropertyValue` for everything the page set. */
171
+ /** `getComputedStyle(el, pseudoElt)`: a live view of the *cascaded* style, so
172
+ * stylesheet rules (not only the inline `style` attribute) are visible. The
173
+ * optional pseudo-element selector ("::before"/"::after") resolves against the
174
+ * generated-content box; any other pseudo-element yields empty values, like a
175
+ * browser does for one the engine did not generate. */
182
176
  xt_value win_get_computed_style(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
183
177
  (void)self;
184
178
  (void)env;
185
- xt_value element = xt_arg(argc, argv, 0);
186
- xt_value style = xt_get(element, xt_string_from_cstr("style"));
187
- return XT_IS_OBJECT(style) ? style : xt_object_new();
179
+ return domapi::computed_style_handle(xt_arg(argc, argv, 0), xt_arg(argc, argv, 1));
188
180
  }
189
181
 
190
182
  xt_value win_alert(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
@@ -196,19 +188,13 @@ xt_value win_alert(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
196
188
  }
197
189
 
198
190
  xt_value win_confirm(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
199
- (void)self;
200
191
  (void)env;
201
- (void)argc;
202
- (void)argv;
203
- return XT_FALSE;
192
+ return xt_browser_confirm(xt_gui_window_from_this(self), argc, argv);
204
193
  }
205
194
 
206
195
  xt_value win_prompt(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
207
- (void)self;
208
196
  (void)env;
209
- (void)argc;
210
- (void)argv;
211
- return XT_UNDEFINED;
197
+ return xt_browser_prompt(xt_gui_window_from_this(self), argc, argv);
212
198
  }
213
199
 
214
200
  /* -- host objects --------------------------------------------------------- */
@@ -253,18 +239,6 @@ xt_value location_build(void) {
253
239
  return location;
254
240
  }
255
241
 
256
- xt_value media_list_build(const std::string &query, bool matches) {
257
- xt_value list = xt_object_new();
258
- set_prop(list, "media", xt_string_from_cstr(query.c_str()));
259
- set_prop(list, "matches", matches ? XT_TRUE : XT_FALSE);
260
- set_method(list, "addListener", (void *)noop);
261
- set_method(list, "removeListener", (void *)noop);
262
- set_method(list, "addEventListener", (void *)noop);
263
- set_method(list, "removeEventListener", (void *)noop);
264
- set_method(list, "dispatchEvent", (void *)noop);
265
- return list;
266
- }
267
-
268
242
  /* -- localStorage --------------------------------------------------------- */
269
243
 
270
244
  xt_value storage_get_item(xt_value self, xt_value env, int32_t argc, xt_value *argv) {
@@ -338,7 +312,12 @@ xt_value host_type_construct(xt_value self, xt_value env, int32_t argc, xt_value
338
312
  /** The prototype a DOM type name should inherit, so `el instanceof Element`
339
313
  * holds for every element handle the engine hands out. */
340
314
  xt_value type_proto(const std::string &name) {
341
- if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent") {
315
+ if (name == "AbortController") return domapi::abort_controller_proto();
316
+ if (name == "AbortSignal") return domapi::abort_signal_proto();
317
+ if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent" ||
318
+ name == "UIEvent" || name == "MouseEvent" || name == "PointerEvent" ||
319
+ name == "WheelEvent" || name == "KeyboardEvent" || name == "FocusEvent" ||
320
+ name == "InputEvent" || name == "SubmitEvent") {
342
321
  return domapi::event_proto();
343
322
  }
344
323
  if (name == "AudioContext" || name == "webkitAudioContext") return xt_gui_audio_proto();
@@ -357,6 +336,7 @@ xt_value type_proto(const std::string &name) {
357
336
  if (name == "WebGLQuery") return domapi::webgl_object_proto("query");
358
337
  if (name == "WebGLSync") return domapi::webgl_object_proto("sync");
359
338
  if (name == "WebGLTransformFeedback") return domapi::webgl_object_proto("transformfeedback");
339
+ if (name == "Path2D") return domapi::path2d_proto();
360
340
  if (name == "ImageBitmap") {
361
341
  /* A bitmap is a plain immutable object, not a DOM node: a node proto would
362
342
  * expose the element width/height accessors and answer 0. */
@@ -394,9 +374,15 @@ xt_value host_type_construct(xt_value self, xt_value env, int32_t argc, xt_value
394
374
  if (name == "AudioContext" || name == "webkitAudioContext") {
395
375
  return xt_gui_audio_create(argc, argv);
396
376
  }
397
- if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent") {
377
+ if (name == "AbortController") return domapi::abort_controller_new();
378
+ if (name == "AbortSignal") return domapi::abort_signal_new(false);
379
+ if (name == "Event" || name == "CustomEvent" || name == "ProgressEvent" ||
380
+ name == "UIEvent" || name == "MouseEvent" || name == "PointerEvent" ||
381
+ name == "WheelEvent" || name == "KeyboardEvent" || name == "FocusEvent" ||
382
+ name == "InputEvent" || name == "SubmitEvent") {
398
383
  return domapi::event_construct(xt_arg(argc, argv, 0), xt_arg(argc, argv, 1));
399
384
  }
385
+ if (name == "Path2D") return domapi::path2d_create(xt_arg(argc, argv, 0));
400
386
  xt_value target = xt_object_new_with_proto(type_proto(name));
401
387
  set_prop(target, "constructor", type_value(name));
402
388
  return target;
@@ -438,10 +424,6 @@ void ensure_singletons(void) {
438
424
  g_location = location_build();
439
425
  xt_gc_add_root(&g_location);
440
426
  }
441
- if (!XT_IS_OBJECT(g_media_list)) {
442
- g_media_list = media_list_build("", false);
443
- xt_gc_add_root(&g_media_list);
444
- }
445
427
  }
446
428
 
447
429
  } // namespace
@@ -474,9 +456,8 @@ void xt_browser_install_window(xt_value proto) {
474
456
  set_method(proto, "confirm", (void *)win_confirm);
475
457
  set_method(proto, "prompt", (void *)win_prompt);
476
458
  set_method(proto, "escape", (void *)xt_browser_escape);
477
- set_method(proto, "focus", (void *)noop);
478
- set_method(proto, "blur", (void *)noop);
479
- set_method(proto, "scrollTo", (void *)noop);
459
+ /* Page scroll and window focus/blur live in `browser_navigation.cpp`. */
460
+ xt_browser_install_window_navigation(proto);
480
461
  }
481
462
 
482
463
  /** `window` / `self`. */
@@ -581,3 +562,7 @@ xt_value xt_browser_alert(int32_t argc, xt_value *argv) {
581
562
  xt_value xt_browser_prompt(int32_t argc, xt_value *argv) {
582
563
  return win_prompt(XT_UNDEFINED, XT_UNDEFINED, argc, argv);
583
564
  }
565
+
566
+ xt_value xt_browser_confirm_global(int32_t argc, xt_value *argv) {
567
+ return win_confirm(XT_UNDEFINED, XT_UNDEFINED, argc, argv);
568
+ }
@@ -0,0 +1,249 @@
1
+ /*
2
+ * xbintsc GUI engine — `AbortController` / `AbortSignal`.
3
+ *
4
+ * The pair exists so `addEventListener(..., { signal })` can actually remove a
5
+ * listener, as the DOM specifies. The engine has no Web-IDL machinery, so both
6
+ * objects are plain runtime objects with a shared hidden state:
7
+ *
8
+ * controller.__xt_gui_abort_state the state object
9
+ * controller.__xt_gui_abort_signal the signal handed out by `.signal`
10
+ * signal.__xt_gui_abort_state the same state object
11
+ *
12
+ * The state holds `aborted`, `reason` and the 'abort' listener list. Every value
13
+ * lives in a runtime object reachable from the handle the page kept, so the
14
+ * mark-sweep collector sees the whole graph.
15
+ */
16
+
17
+ #include "dom_api_internal.h"
18
+ #include "listener_options.h"
19
+
20
+ namespace domapi {
21
+
22
+ xt_value abort_signal_proto();
23
+ xt_value abort_controller_proto();
24
+
25
+ namespace {
26
+
27
+ xt_value new_state() {
28
+ xt_value state = xt_object_new();
29
+ xt_object_set(state, xt_string_from_cstr("aborted"), XT_FALSE);
30
+ xt_object_set(state, xt_string_from_cstr("reason"), XT_UNDEFINED);
31
+ xt_object_set(state, xt_string_from_cstr("listeners"), xt_array_new(0, nullptr));
32
+ return state;
33
+ }
34
+
35
+ xt_value state_of(xt_value self) { return xt_object_get_cstr(self, "__xt_gui_abort_state"); }
36
+
37
+ xt_value signal_of(xt_value self) { return xt_object_get_cstr(self, "__xt_gui_abort_signal"); }
38
+
39
+ bool state_aborted(xt_value state) {
40
+ return XT_IS_OBJECT(state) && value_truthy(xt_object_get_cstr(state, "aborted"));
41
+ }
42
+
43
+ xt_value abort_signal_from_state(xt_value state, xt_value controller) {
44
+ xt_value signal = xt_object_new_with_proto(abort_signal_proto());
45
+ xt_object_set(signal, xt_string_from_cstr("__xt_gui_abort_state"), state);
46
+ if (!XT_IS_UNDEFINED(controller)) {
47
+ xt_object_set(signal, xt_string_from_cstr("__xt_gui_abort_controller"), controller);
48
+ }
49
+ return signal;
50
+ }
51
+
52
+ /** Call `fn` (or `{ handleEvent }`) with an `{ type, target }` event. */
53
+ void call_abort_listener(xt_value fn, xt_value signal) {
54
+ xt_value event = xt_object_new_with_proto(event_proto());
55
+ xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr("abort"));
56
+ xt_object_set(event, xt_string_from_cstr("target"), signal);
57
+ xt_object_set(event, xt_string_from_cstr("currentTarget"), signal);
58
+ xt_object_set(event, xt_string_from_cstr("defaultPrevented"), XT_FALSE);
59
+ if (XT_IS_FUNCTION(fn)) {
60
+ xt_value args[1] = {event};
61
+ xt_call_with_this(fn, signal, 1, args);
62
+ return;
63
+ }
64
+ if (XT_IS_OBJECT(fn)) {
65
+ xt_value handler = xt_object_get_cstr(fn, "handleEvent");
66
+ if (XT_IS_FUNCTION(handler)) {
67
+ xt_value args[1] = {event};
68
+ xt_call_with_this(handler, fn, 1, args);
69
+ }
70
+ }
71
+ }
72
+
73
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
74
+
75
+ METHOD(signal_aborted_get) {
76
+ (void)env;
77
+ (void)argc;
78
+ (void)argv;
79
+ return state_aborted(state_of(self)) ? XT_TRUE : XT_FALSE;
80
+ }
81
+
82
+ METHOD(signal_reason_get) {
83
+ (void)env;
84
+ (void)argc;
85
+ (void)argv;
86
+ xt_value state = state_of(self);
87
+ if (!XT_IS_OBJECT(state)) return XT_UNDEFINED;
88
+ return xt_object_get_cstr(state, "reason");
89
+ }
90
+
91
+ METHOD(signal_onabort_get) {
92
+ (void)env;
93
+ (void)argc;
94
+ (void)argv;
95
+ return xt_object_get_cstr(self, "__xt_gui_onabort");
96
+ }
97
+
98
+ METHOD(signal_onabort_set) {
99
+ (void)env;
100
+ if (argc >= 1) xt_object_set(self, xt_string_from_cstr("__xt_gui_onabort"), xt_arg(argc, argv, 0));
101
+ return XT_UNDEFINED;
102
+ }
103
+
104
+ /** `signal.addEventListener("abort", fn, options?)` — only 'abort' exists. */
105
+ METHOD(signal_add_listener) {
106
+ (void)env;
107
+ xt_value state = state_of(self);
108
+ if (!XT_IS_OBJECT(state) || argc < 2) return XT_UNDEFINED;
109
+ std::string type = to_string_value(xt_arg(argc, argv, 0));
110
+ if (type != "abort") return XT_UNDEFINED;
111
+ xt_value fn = xt_arg(argc, argv, 1);
112
+ if (!XT_IS_FUNCTION(fn) && !XT_IS_OBJECT(fn)) return XT_UNDEFINED;
113
+ XtGuiListenerOptions options = xt_gui_listener_options(xt_arg(argc, argv, 2));
114
+ xt_value entry = xt_object_new();
115
+ xt_object_set(entry, xt_string_from_cstr("fn"), fn);
116
+ xt_object_set(entry, xt_string_from_cstr("once"), options.once ? XT_TRUE : XT_FALSE);
117
+ xt_array_push(xt_object_get_cstr(state, "listeners"), entry);
118
+ return XT_UNDEFINED;
119
+ }
120
+
121
+ METHOD(signal_remove_listener) {
122
+ (void)env;
123
+ xt_value state = state_of(self);
124
+ if (!XT_IS_OBJECT(state) || argc < 2) return XT_UNDEFINED;
125
+ xt_value listeners = xt_object_get_cstr(state, "listeners");
126
+ if (!XT_IS_ARRAY(listeners)) return XT_UNDEFINED;
127
+ xt_value fn = xt_arg(argc, argv, 1);
128
+ xt_value kept = xt_array_new(0, nullptr);
129
+ int32_t count = (int32_t)xt_to_number(xt_array_length(listeners));
130
+ for (int32_t i = 0; i < count; i++) {
131
+ xt_value entry = xt_array_get(listeners, xt_number((double)i));
132
+ if (!xt_truthy(xt_seq(xt_object_get_cstr(entry, "fn"), fn))) xt_array_push(kept, entry);
133
+ }
134
+ xt_object_set(state, xt_string_from_cstr("listeners"), kept);
135
+ return XT_UNDEFINED;
136
+ }
137
+
138
+ /** `signal.throwIfAborted()`: raise the abort reason (an `Error` by default). */
139
+ METHOD(signal_throw_if_aborted) {
140
+ (void)env;
141
+ (void)argc;
142
+ (void)argv;
143
+ xt_value state = state_of(self);
144
+ if (!state_aborted(state)) return XT_UNDEFINED;
145
+ xt_value reason = xt_object_get_cstr(state, "reason");
146
+ if (XT_IS_UNDEFINED(reason) || reason == XT_NULL) {
147
+ reason = xt_error_ctor_by_name(xt_string_from_cstr("Error"), 0, nullptr);
148
+ }
149
+ xt_throw(reason);
150
+ return XT_UNDEFINED;
151
+ }
152
+
153
+ METHOD(controller_signal_get) {
154
+ (void)env;
155
+ (void)argc;
156
+ (void)argv;
157
+ xt_value signal = signal_of(self);
158
+ if (XT_IS_OBJECT(signal)) return signal;
159
+ xt_value state = state_of(self);
160
+ if (!XT_IS_OBJECT(state)) {
161
+ state = new_state();
162
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_abort_state"), state);
163
+ }
164
+ signal = abort_signal_from_state(state, self);
165
+ xt_object_set(self, xt_string_from_cstr("__xt_gui_abort_signal"), signal);
166
+ return signal;
167
+ }
168
+
169
+ /** `controller.abort(reason)`: flip the state and dispatch 'abort' once. */
170
+ METHOD(controller_abort) {
171
+ (void)env;
172
+ xt_value state = state_of(self);
173
+ if (!XT_IS_OBJECT(state) || state_aborted(state)) return XT_UNDEFINED;
174
+ xt_value reason = argc >= 1 ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
175
+ if (XT_IS_UNDEFINED(reason)) {
176
+ xt_value message = xt_string_from_cstr("This operation was aborted");
177
+ reason = xt_error_ctor_by_name(xt_string_from_cstr("Error"), 1, &message);
178
+ }
179
+ xt_object_set(state, xt_string_from_cstr("aborted"), XT_TRUE);
180
+ xt_object_set(state, xt_string_from_cstr("reason"), reason);
181
+ xt_value signal = signal_of(self);
182
+ if (!XT_IS_OBJECT(signal)) signal = controller_signal_get(self, XT_UNDEFINED, 0, nullptr);
183
+ xt_value onabort = xt_object_get_cstr(signal, "__xt_gui_onabort");
184
+ if (XT_IS_FUNCTION(onabort)) call_abort_listener(onabort, signal);
185
+ xt_value listeners = xt_object_get_cstr(state, "listeners");
186
+ int32_t count = XT_IS_ARRAY(listeners) ? (int32_t)xt_to_number(xt_array_length(listeners)) : 0;
187
+ xt_value snapshot = xt_array_new(0, nullptr);
188
+ for (int32_t i = 0; i < count; i++) {
189
+ xt_array_push(snapshot, xt_array_get(listeners, xt_number((double)i)));
190
+ }
191
+ xt_object_set(state, xt_string_from_cstr("listeners"), xt_array_new(0, nullptr));
192
+ for (int32_t i = 0; i < count; i++) {
193
+ xt_value entry = xt_array_get(snapshot, xt_number((double)i));
194
+ call_abort_listener(xt_object_get_cstr(entry, "fn"), signal);
195
+ }
196
+ return XT_UNDEFINED;
197
+ }
198
+
199
+ #undef METHOD
200
+
201
+ } // namespace
202
+
203
+ xt_value abort_signal_proto() {
204
+ static xt_value proto = XT_UNDEFINED;
205
+ if (XT_IS_OBJECT(proto)) return proto;
206
+ proto = xt_object_new();
207
+ xt_gc_add_root(&proto);
208
+ xt_object_define_getter(proto, xt_string_from_cstr("aborted"),
209
+ xt_closure_new((void *)signal_aborted_get, 0, nullptr));
210
+ xt_object_define_getter(proto, xt_string_from_cstr("reason"),
211
+ xt_closure_new((void *)signal_reason_get, 0, nullptr));
212
+ xt_object_define_getter(proto, xt_string_from_cstr("onabort"),
213
+ xt_closure_new((void *)signal_onabort_get, 0, nullptr));
214
+ xt_object_define_setter(proto, xt_string_from_cstr("onabort"),
215
+ xt_closure_new((void *)signal_onabort_set, 0, nullptr));
216
+ xt_object_set(proto, xt_string_from_cstr("addEventListener"),
217
+ xt_closure_new((void *)signal_add_listener, 0, nullptr));
218
+ xt_object_set(proto, xt_string_from_cstr("removeEventListener"),
219
+ xt_closure_new((void *)signal_remove_listener, 0, nullptr));
220
+ xt_object_set(proto, xt_string_from_cstr("throwIfAborted"),
221
+ xt_closure_new((void *)signal_throw_if_aborted, 0, nullptr));
222
+ return proto;
223
+ }
224
+
225
+ xt_value abort_controller_proto() {
226
+ static xt_value proto = XT_UNDEFINED;
227
+ if (XT_IS_OBJECT(proto)) return proto;
228
+ proto = xt_object_new();
229
+ xt_gc_add_root(&proto);
230
+ xt_object_define_getter(proto, xt_string_from_cstr("signal"),
231
+ xt_closure_new((void *)controller_signal_get, 0, nullptr));
232
+ xt_object_set(proto, xt_string_from_cstr("abort"),
233
+ xt_closure_new((void *)controller_abort, 0, nullptr));
234
+ return proto;
235
+ }
236
+
237
+ xt_value abort_controller_new() {
238
+ xt_value controller = xt_object_new_with_proto(abort_controller_proto());
239
+ xt_object_set(controller, xt_string_from_cstr("__xt_gui_abort_state"), new_state());
240
+ return controller;
241
+ }
242
+
243
+ xt_value abort_signal_new(bool aborted) {
244
+ xt_value state = new_state();
245
+ if (aborted) xt_object_set(state, xt_string_from_cstr("aborted"), XT_TRUE);
246
+ return abort_signal_from_state(state, XT_UNDEFINED);
247
+ }
248
+
249
+ } // namespace domapi
@@ -0,0 +1,76 @@
1
+ /*
2
+ * xbintsc GUI engine — `window.confirm` / `window.prompt`.
3
+ *
4
+ * A compiled page has no browser chrome, so a blocking dialog is opt-in: with
5
+ * `XT_GUI_DIALOGS=1` and a live window the engine shows a native SDL message
6
+ * box. Otherwise (and always for `prompt`, which would need a text input) the
7
+ * message is written to stderr and the documented default is returned — the
8
+ * arguments are never silently dropped. `confirm` answers false and `prompt`
9
+ * answers `defaultValue` (or "") when no dialog is shown.
10
+ */
11
+
12
+ #include "gui_engine.h"
13
+
14
+ #include <stdio.h>
15
+ #include <string>
16
+
17
+ namespace {
18
+
19
+ std::string message_of(int32_t argc, xt_value *argv) {
20
+ if (argc < 1) return std::string();
21
+ const char *data = xt_string_data(xt_to_string(xt_arg(argc, argv, 0)));
22
+ return data != nullptr ? data : "";
23
+ }
24
+
25
+ bool dialogs_enabled() {
26
+ const char *value = SDL_getenv("XT_GUI_DIALOGS");
27
+ return value != nullptr && value[0] == '1';
28
+ }
29
+
30
+ bool show_message(XtGuiWindow *win, const std::string &message, bool with_cancel) {
31
+ if (win == nullptr || win->window == nullptr) return false;
32
+ SDL_MessageBoxButtonData buttons[2];
33
+ SDL_zero(buttons);
34
+ int count = 1;
35
+ buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
36
+ buttons[0].buttonID = 1;
37
+ buttons[0].text = "OK";
38
+ if (with_cancel) {
39
+ buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
40
+ buttons[1].buttonID = 0;
41
+ buttons[1].text = "Cancel";
42
+ count = 2;
43
+ }
44
+ SDL_MessageBoxData data;
45
+ SDL_zero(data);
46
+ data.flags = SDL_MESSAGEBOX_INFORMATION;
47
+ data.window = win->window;
48
+ data.title = "xbintsc";
49
+ data.message = message.c_str();
50
+ data.numbuttons = count;
51
+ data.buttons = buttons;
52
+ int pressed = -1;
53
+ if (!SDL_ShowMessageBox(&data, &pressed)) return false;
54
+ return pressed == 1;
55
+ }
56
+
57
+ } // namespace
58
+
59
+ xt_value xt_browser_confirm(XtGuiWindow *win, int32_t argc, xt_value *argv) {
60
+ std::string message = message_of(argc, argv);
61
+ if (dialogs_enabled() && show_message(win, message, true)) return XT_TRUE;
62
+ fprintf(stderr, "confirm: %s\n", message.c_str());
63
+ return XT_FALSE;
64
+ }
65
+
66
+ xt_value xt_browser_prompt(XtGuiWindow *win, int32_t argc, xt_value *argv) {
67
+ std::string message = message_of(argc, argv);
68
+ std::string fallback;
69
+ if (argc >= 2) {
70
+ const char *data = xt_string_data(xt_to_string(xt_arg(argc, argv, 1)));
71
+ if (data != nullptr) fallback = data;
72
+ }
73
+ if (dialogs_enabled()) show_message(win, message, false);
74
+ fprintf(stderr, "prompt: %s\n", message.c_str());
75
+ return xt_string_from_cstr(fallback.c_str());
76
+ }