xbintsc 0.5.32 → 0.5.37

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 (222) hide show
  1. package/README.md +10 -7
  2. package/README.zh-CN.md +10 -8
  3. package/dist/scripts/check-version.d.ts +27 -0
  4. package/dist/scripts/check-version.js +71 -0
  5. package/dist/scripts/check-version.js.map +1 -0
  6. package/dist/scripts/package-platform.d.ts +58 -0
  7. package/dist/scripts/package-platform.js +142 -0
  8. package/dist/scripts/package-platform.js.map +1 -0
  9. package/dist/scripts/verify-platform-package.d.ts +34 -0
  10. package/dist/scripts/verify-platform-package.js +161 -0
  11. package/dist/scripts/verify-platform-package.js.map +1 -0
  12. package/dist/src/cli/help.d.ts +1 -1
  13. package/dist/src/cli/main.js +3 -1
  14. package/dist/src/cli/main.js.map +1 -1
  15. package/dist/src/driver/runtime-lib.d.ts +29 -4
  16. package/dist/src/driver/runtime-lib.js +68 -10
  17. package/dist/src/driver/runtime-lib.js.map +1 -1
  18. package/dist/src/driver/version.d.ts +1 -1
  19. package/dist/src/driver/version.js +1 -1
  20. package/dist/src/extensions/gui/index.js +18 -5
  21. package/dist/src/extensions/gui/index.js.map +1 -1
  22. package/dist/src/extensions/media/index.js +4 -3
  23. package/dist/src/extensions/media/index.js.map +1 -1
  24. package/dist/src/parser/context.d.ts +41 -0
  25. package/dist/src/parser/context.js +89 -4
  26. package/dist/src/parser/context.js.map +1 -1
  27. package/dist/src/parser/literals.js +6 -12
  28. package/dist/src/parser/literals.js.map +1 -1
  29. package/dist/tests/driver/platform-package.test.d.ts +1 -0
  30. package/dist/tests/driver/platform-package.test.js +49 -0
  31. package/dist/tests/driver/platform-package.test.js.map +1 -0
  32. package/dist/tests/driver/runtime-lib.test.js +51 -4
  33. package/dist/tests/driver/runtime-lib.test.js.map +1 -1
  34. package/dist/tests/e2e/gui-audio.test.d.ts +14 -0
  35. package/dist/tests/e2e/gui-audio.test.js +287 -0
  36. package/dist/tests/e2e/gui-audio.test.js.map +1 -0
  37. package/dist/tests/e2e/gui-browser.test.js +65 -0
  38. package/dist/tests/e2e/gui-browser.test.js.map +1 -1
  39. package/dist/tests/e2e/gui-canvas-args.test.d.ts +6 -0
  40. package/dist/tests/e2e/gui-canvas-args.test.js +217 -0
  41. package/dist/tests/e2e/gui-canvas-args.test.js.map +1 -0
  42. package/dist/tests/e2e/gui-canvas.test.js.map +1 -1
  43. package/dist/tests/e2e/gui-dom.test.js +215 -0
  44. package/dist/tests/e2e/gui-dom.test.js.map +1 -1
  45. package/dist/tests/e2e/gui-events.test.js +112 -0
  46. package/dist/tests/e2e/gui-events.test.js.map +1 -1
  47. package/dist/tests/e2e/gui-helpers.d.ts +7 -0
  48. package/dist/tests/e2e/gui-helpers.js +1 -0
  49. package/dist/tests/e2e/gui-helpers.js.map +1 -1
  50. package/dist/tests/e2e/gui-media.test.js +78 -0
  51. package/dist/tests/e2e/gui-media.test.js.map +1 -1
  52. package/dist/tests/e2e/gui-style.test.js +55 -0
  53. package/dist/tests/e2e/gui-style.test.js.map +1 -1
  54. package/dist/tests/e2e/gui-webgl.test.js +258 -0
  55. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  56. package/dist/tests/e2e/gui-webgl2-3d.test.d.ts +4 -0
  57. package/dist/tests/e2e/gui-webgl2-3d.test.js +264 -0
  58. package/dist/tests/e2e/gui-webgl2-3d.test.js.map +1 -0
  59. package/dist/tests/e2e/gui-webgl2-blit.test.d.ts +10 -0
  60. package/dist/tests/e2e/gui-webgl2-blit.test.js +262 -0
  61. package/dist/tests/e2e/gui-webgl2-blit.test.js.map +1 -0
  62. package/dist/tests/e2e/gui-webgl2-formats.test.d.ts +6 -0
  63. package/dist/tests/e2e/gui-webgl2-formats.test.js +395 -0
  64. package/dist/tests/e2e/gui-webgl2-formats.test.js.map +1 -0
  65. package/dist/tests/e2e/gui-webgl2-mrt.test.d.ts +4 -0
  66. package/dist/tests/e2e/gui-webgl2-mrt.test.js +105 -0
  67. package/dist/tests/e2e/gui-webgl2-mrt.test.js.map +1 -0
  68. package/dist/tests/e2e/gui-webgl2-ubo.test.d.ts +8 -0
  69. package/dist/tests/e2e/gui-webgl2-ubo.test.js +112 -0
  70. package/dist/tests/e2e/gui-webgl2-ubo.test.js.map +1 -0
  71. package/dist/tests/e2e/gui-webgl2.test.js +45 -1
  72. package/dist/tests/e2e/gui-webgl2.test.js.map +1 -1
  73. package/dist/tests/extensions/gui-index.test.js +1 -0
  74. package/dist/tests/extensions/gui-index.test.js.map +1 -1
  75. package/dist/tests/extensions/media.test.js +1 -0
  76. package/dist/tests/extensions/media.test.js.map +1 -1
  77. package/dist/tests/parser/expressions.test.js +30 -0
  78. package/dist/tests/parser/expressions.test.js.map +1 -1
  79. package/dist/tests/platform-tarball.test.d.ts +1 -0
  80. package/dist/tests/platform-tarball.test.js +111 -0
  81. package/dist/tests/platform-tarball.test.js.map +1 -0
  82. package/dist/tests/version.test.js +6 -0
  83. package/dist/tests/version.test.js.map +1 -1
  84. package/doc/ai/contributing.md +11 -0
  85. package/doc/ai/extensions.md +7 -4
  86. package/doc/ai/troubleshooting.md +7 -5
  87. package/doc/ai/zh-CN/contributing.md +9 -0
  88. package/doc/ai/zh-CN/extensions.md +4 -2
  89. package/doc/ai/zh-CN/troubleshooting.md +5 -3
  90. package/doc/gui/api.md +99 -22
  91. package/doc/gui/build.md +11 -4
  92. package/doc/gui/canvas.md +49 -18
  93. package/doc/gui/dom.md +57 -7
  94. package/doc/gui/media.md +4 -0
  95. package/doc/gui/webgl.md +105 -46
  96. package/doc/gui.md +7 -3
  97. package/doc/npm-package.md +55 -27
  98. package/doc/requirements.md +6 -2
  99. package/doc/zh-CN/gui/api.md +84 -14
  100. package/doc/zh-CN/gui/build.md +7 -2
  101. package/doc/zh-CN/gui/canvas.md +44 -19
  102. package/doc/zh-CN/gui/dom.md +48 -4
  103. package/doc/zh-CN/gui/media.md +3 -0
  104. package/doc/zh-CN/gui/webgl.md +22 -16
  105. package/doc/zh-CN/gui.md +3 -2
  106. package/doc/zh-CN/npm-package.md +44 -23
  107. package/doc/zh-CN/requirements.md +4 -1
  108. package/package.json +17 -1
  109. package/runtime/ext_gui/audio.cpp +365 -153
  110. package/runtime/ext_gui/audio_buffer.cpp +166 -0
  111. package/runtime/ext_gui/audio_buffer.h +37 -0
  112. package/runtime/ext_gui/audio_decode.cpp +219 -0
  113. package/runtime/ext_gui/audio_decode.h +39 -0
  114. package/runtime/ext_gui/audio_device.cpp +87 -0
  115. package/runtime/ext_gui/audio_effects.cpp +163 -0
  116. package/runtime/ext_gui/audio_effects.h +25 -0
  117. package/runtime/ext_gui/audio_internal.h +30 -0
  118. package/runtime/ext_gui/audio_mixer.cpp +494 -0
  119. package/runtime/ext_gui/audio_mixer.h +238 -0
  120. package/runtime/ext_gui/audio_param.cpp +104 -0
  121. package/runtime/ext_gui/audio_param_object.cpp +155 -0
  122. package/runtime/ext_gui/browser.cpp +45 -50
  123. package/runtime/ext_gui/browser_abort.cpp +249 -0
  124. package/runtime/ext_gui/browser_dialogs.cpp +76 -0
  125. package/runtime/ext_gui/browser_media.cpp +176 -0
  126. package/runtime/ext_gui/browser_navigation.cpp +130 -0
  127. package/runtime/ext_gui/canvas.cpp +88 -30
  128. package/runtime/ext_gui/canvas.h +74 -9
  129. package/runtime/ext_gui/canvas_image_data.cpp +143 -0
  130. package/runtime/ext_gui/canvas_internal.h +8 -5
  131. package/runtime/ext_gui/canvas_pattern.cpp +90 -0
  132. package/runtime/ext_gui/canvas_raster.cpp +31 -9
  133. package/runtime/ext_gui/canvas_style.cpp +4 -2
  134. package/runtime/ext_gui/css_selector.cpp +21 -3
  135. package/runtime/ext_gui/dom.h +4 -0
  136. package/runtime/ext_gui/dom_api.cpp +1 -0
  137. package/runtime/ext_gui/dom_api_canvas.cpp +120 -114
  138. package/runtime/ext_gui/dom_api_canvas_extra.cpp +57 -2
  139. package/runtime/ext_gui/dom_api_canvas_image.cpp +239 -0
  140. package/runtime/ext_gui/dom_api_canvas_paths.cpp +179 -0
  141. package/runtime/ext_gui/dom_api_computed_style.cpp +124 -0
  142. package/runtime/ext_gui/dom_api_dataset.cpp +154 -0
  143. package/runtime/ext_gui/dom_api_document.cpp +58 -5
  144. package/runtime/ext_gui/dom_api_events.cpp +93 -17
  145. package/runtime/ext_gui/dom_api_internal.h +71 -2
  146. package/runtime/ext_gui/dom_api_mutation.cpp +159 -0
  147. package/runtime/ext_gui/dom_api_node.cpp +40 -19
  148. package/runtime/ext_gui/dom_api_node_read.cpp +37 -1
  149. package/runtime/ext_gui/dom_api_path2d.cpp +358 -0
  150. package/runtime/ext_gui/dom_api_proto.cpp +35 -0
  151. package/runtime/ext_gui/dom_api_style.cpp +223 -13
  152. package/runtime/ext_gui/dom_api_webgl.cpp +7 -0
  153. package/runtime/ext_gui/dom_api_webgl2.cpp +151 -36
  154. package/runtime/ext_gui/dom_api_webgl2_draw.cpp +3 -3
  155. package/runtime/ext_gui/dom_api_webgl_buffer.cpp +6 -2
  156. package/runtime/ext_gui/dom_api_webgl_compressed.cpp +167 -0
  157. package/runtime/ext_gui/dom_api_webgl_framebuffer.cpp +160 -0
  158. package/runtime/ext_gui/dom_api_webgl_shader.cpp +4 -4
  159. package/runtime/ext_gui/dom_api_webgl_source.cpp +161 -0
  160. package/runtime/ext_gui/dom_api_webgl_state.cpp +169 -23
  161. package/runtime/ext_gui/dom_api_webgl_texture.cpp +276 -236
  162. package/runtime/ext_gui/dom_api_webgl_ubo.cpp +81 -0
  163. package/runtime/ext_gui/gui.cpp +103 -14
  164. package/runtime/ext_gui/gui_engine.h +56 -4
  165. package/runtime/ext_gui/gui_events.cpp +80 -9
  166. package/runtime/ext_gui/image_bitmap.cpp +28 -0
  167. package/runtime/ext_gui/image_bitmap.h +26 -0
  168. package/runtime/ext_gui/image_bitmap_browser.cpp +136 -0
  169. package/runtime/ext_gui/listener_options.h +43 -0
  170. package/runtime/ext_gui/media.cpp +32 -0
  171. package/runtime/ext_gui/media.h +13 -0
  172. package/runtime/ext_gui/media_api.h +23 -0
  173. package/runtime/ext_gui/paint.cpp +28 -0
  174. package/runtime/ext_gui/paint.h +4 -0
  175. package/runtime/ext_gui/webgl.h +152 -18
  176. package/runtime/ext_gui/webgl_blit.cpp +95 -0
  177. package/runtime/ext_gui/webgl_buffer.cpp +178 -263
  178. package/runtime/ext_gui/webgl_buffer_objects.cpp +270 -0
  179. package/runtime/ext_gui/webgl_clear.cpp +142 -0
  180. package/runtime/ext_gui/webgl_compressed.cpp +180 -0
  181. package/runtime/ext_gui/webgl_context.cpp +241 -156
  182. package/runtime/ext_gui/webgl_draw.cpp +68 -3
  183. package/runtime/ext_gui/webgl_draw_buffers.cpp +75 -0
  184. package/runtime/ext_gui/webgl_glsl.h +40 -0
  185. package/runtime/ext_gui/webgl_glsl_compile.cpp +6 -0
  186. package/runtime/ext_gui/webgl_glsl_rewrite.cpp +18 -0
  187. package/runtime/ext_gui/webgl_glsl_scan.cpp +20 -11
  188. package/runtime/ext_gui/webgl_glsl_scan_block.cpp +126 -0
  189. package/runtime/ext_gui/webgl_glsl_tokens.h +28 -0
  190. package/runtime/ext_gui/webgl_pipeline.cpp +71 -19
  191. package/runtime/ext_gui/webgl_program_blocks.cpp +58 -0
  192. package/runtime/ext_gui/webgl_program_internal.h +6 -0
  193. package/runtime/ext_gui/webgl_shader.cpp +13 -1
  194. package/runtime/ext_gui/webgl_state.cpp +228 -0
  195. package/runtime/ext_gui/webgl_texture.cpp +144 -17
  196. package/runtime/ext_gui/webgl_texture_3d.cpp +215 -0
  197. package/runtime/ext_gui/webgl_uniform_block.h +31 -0
  198. package/runtime/ext_gui/webgl_webgl2.cpp +58 -5
  199. package/runtime/ext_gui/window.cpp +14 -65
  200. package/runtime/ext_gui/window_events.cpp +125 -0
  201. package/runtime/ext_media/decode_audio.cpp +308 -0
  202. package/runtime/ext_media/media_bridge.c +2 -0
  203. package/runtime/rt.h +43 -0
  204. package/runtime/rt_internal.h +4 -0
  205. package/runtime/xt_array_buffer.c +30 -0
  206. package/runtime/xt_builtins.c +0 -21
  207. package/runtime/xt_containers/access.inc +94 -2
  208. package/runtime/xt_containers/objects.inc +29 -31
  209. package/runtime/xt_typed_array/elements.inc +13 -0
  210. package/scripts/build-media.ts +14 -6
  211. package/scripts/build-runtime.ts +21 -16
  212. package/scripts/check-version.ts +39 -9
  213. package/scripts/package-platform.ts +183 -0
  214. package/scripts/set-platform-version.ts +62 -0
  215. package/scripts/verify-platform-package.ts +182 -0
  216. package/src/cli/main.ts +5 -1
  217. package/src/driver/runtime-lib.ts +73 -9
  218. package/src/driver/version.ts +1 -1
  219. package/src/extensions/gui/index.ts +18 -5
  220. package/src/extensions/media/index.ts +4 -3
  221. package/src/parser/context.ts +99 -4
  222. package/src/parser/literals.ts +6 -12
@@ -93,17 +93,31 @@ METHOD(doc_create_element) {
93
93
  return xt_gui_node_handle(win, win->document->createElement(tag));
94
94
  }
95
95
 
96
- /** `createElementNS(ns, name)`: the engine has no namespaces, so the qualified
97
- * name after `:` is what gets created (`createElementNS(svgNS, "svg:rect")`). */
96
+ /** `createElementNS(ns, name)`: the namespace is recorded (so `namespaceURI`
97
+ * answers it) and the qualified name's prefix is preserved. HTML names are
98
+ * lowercased, but names in another namespace keep their case — SVG is
99
+ * case-sensitive (`linearGradient`, `clipPath`). */
98
100
  METHOD(doc_create_element_ns) {
99
101
  (void)env;
100
102
  XtGuiWindow *win = resolve_window(self);
101
103
  if (win == nullptr || win->document == nullptr || argc < 2) return XT_UNDEFINED;
104
+ std::string ns = to_string_value(xt_arg(argc, argv, 0));
102
105
  std::string name = to_string_value(xt_arg(argc, argv, 1));
106
+ std::string prefix;
103
107
  size_t colon = name.rfind(':');
104
- if (colon != std::string::npos) name = name.substr(colon + 1);
105
- for (char &c : name) c = (char)std::tolower((unsigned char)c);
106
- return xt_gui_node_handle(win, win->document->createElement(name));
108
+ if (colon != std::string::npos) {
109
+ prefix = name.substr(0, colon);
110
+ name = name.substr(colon + 1);
111
+ }
112
+ const bool html = ns.empty() || ns == "http://www.w3.org/1999/xhtml";
113
+ if (html) {
114
+ for (char &c : name) c = (char)std::tolower((unsigned char)c);
115
+ }
116
+ xtgui::Node *node = win->document->createElement(name);
117
+ if (node == nullptr) return XT_UNDEFINED;
118
+ node->ns = ns;
119
+ node->ns_prefix = prefix;
120
+ return xt_gui_node_handle(win, node);
107
121
  }
108
122
 
109
123
  /** `document.hidden` / `visibilityState`: a window in the main loop is always
@@ -124,6 +138,15 @@ METHOD(doc_visibility_state_get) {
124
138
  return xt_string_from_cstr("visible");
125
139
  }
126
140
 
141
+ /** `document.hasFocus()`: true after `window.focus()` until `window.blur()`. */
142
+ METHOD(doc_has_focus) {
143
+ (void)env;
144
+ (void)argc;
145
+ (void)argv;
146
+ XtGuiWindow *win = resolve_window(self);
147
+ return win != nullptr && win->focused ? XT_TRUE : XT_FALSE;
148
+ }
149
+
127
150
  /** `document.readyState`: the page is fully parsed before any script runs. */
128
151
  METHOD(doc_ready_state_get) {
129
152
  (void)self;
@@ -149,6 +172,36 @@ METHOD(doc_title_get) {
149
172
  return xt_string_from_cstr(text.c_str());
150
173
  }
151
174
 
175
+ /** `document.title = text`: replace the text of the document's `<title>`.
176
+ * A page without one gets a `<title>` (and a `<head>`, if the parser did not
177
+ * create one), so the assignment is not silently lost. */
178
+ METHOD(doc_title_set) {
179
+ (void)env;
180
+ XtGuiWindow *win = resolve_window(self);
181
+ if (win == nullptr || win->document == nullptr) return XT_UNDEFINED;
182
+ std::string text;
183
+ if (argc >= 1) text = to_string_value(xt_arg(argc, argv, 0));
184
+ xtgui::Node *root = const_cast<xtgui::Node *>(win->document->root());
185
+ xtgui::Node *title = const_cast<xtgui::Node *>(find_by_tag(root, "title"));
186
+ if (title == nullptr) {
187
+ xtgui::Node *head = const_cast<xtgui::Node *>(find_by_tag(root, "head"));
188
+ if (head == nullptr) {
189
+ head = win->document->createElement("head");
190
+ if (head == nullptr) return XT_UNDEFINED;
191
+ /* `<head>` belongs before the body, not after it. */
192
+ xtgui::Node *body = const_cast<xtgui::Node *>(find_by_tag(root, "body"));
193
+ win->document->insertBefore(root, head, body);
194
+ }
195
+ title = win->document->createElement("title");
196
+ if (title == nullptr) return XT_UNDEFINED;
197
+ win->document->appendChild(head, title);
198
+ }
199
+ title->children.clear();
200
+ if (!text.empty()) title->addText(text);
201
+ mark_dirty(win);
202
+ return XT_UNDEFINED;
203
+ }
204
+
152
205
  METHOD(doc_default_view_get) {
153
206
  (void)env;
154
207
  (void)argc;
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  #include "dom_api_internal.h"
7
+ #include "listener_options.h"
7
8
 
8
9
  #include <string>
9
10
  #include <vector>
@@ -15,8 +16,14 @@ static xt_value make_dom_event(XtGuiWindow *win, const char *type, const xtgui::
15
16
  xt_value event = xt_object_new_with_proto(event_proto());
16
17
  xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr(type));
17
18
  xt_object_set(event, xt_string_from_cstr("target"), xt_gui_node_handle(win, target));
19
+ xt_object_set(event, xt_string_from_cstr("currentTarget"), XT_NULL);
18
20
  xt_object_set(event, xt_string_from_cstr("defaultPrevented"), XT_FALSE);
19
21
  xt_object_set(event, xt_string_from_cstr("bubbles"), XT_TRUE);
22
+ xt_object_set(event, xt_string_from_cstr("cancelable"), XT_TRUE);
23
+ xt_object_set(event, xt_string_from_cstr("composed"), XT_TRUE);
24
+ xt_object_set(event, xt_string_from_cstr("isTrusted"), XT_TRUE);
25
+ xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number(0));
26
+ xt_object_set(event, xt_string_from_cstr("timeStamp"), xt_number((double)SDL_GetTicks()));
20
27
  if (XT_IS_OBJECT(legacy)) {
21
28
  static const char *const fields[] = {
22
29
  "x", "y", "clientX", "clientY", "button", "clicks",
@@ -31,23 +38,43 @@ static xt_value make_dom_event(XtGuiWindow *win, const char *type, const xtgui::
31
38
  return event;
32
39
  }
33
40
 
34
- /** A detached event object, as `new Event(type)` / `new CustomEvent(type, init)`
35
- * builds: no target yet, but the standard fields and `detail`. */
41
+ /** Set `field` to `value` unless `init` already supplied one. */
42
+ static void default_field(xt_value event, const char *field, xt_value value) {
43
+ if (XT_IS_UNDEFINED(xt_object_get_cstr(event, field))) {
44
+ xt_object_set(event, xt_string_from_cstr(field), value);
45
+ }
46
+ }
47
+
48
+ /** A detached event object, as `new Event(type, init)` /
49
+ * `new CustomEvent(type, init)` (and the other `*Event` constructors) builds:
50
+ * no target yet, the standard fields, and every member of `init` — so
51
+ * `composed`, `detail` and the mouse/keyboard fields all survive construction
52
+ * instead of being silently dropped. */
36
53
  xt_value event_construct(xt_value type, xt_value init) {
37
54
  std::string name = to_string_value(type);
38
55
  xt_value event = xt_object_new_with_proto(event_proto());
56
+ if (XT_IS_OBJECT(init)) {
57
+ xt_value keys = xt_object_keys(init);
58
+ int32_t count = (int32_t)xt_to_number(xt_array_length(keys));
59
+ for (int32_t i = 0; i < count; i++) {
60
+ xt_value key = xt_array_get(keys, xt_number((double)i));
61
+ xt_object_set(event, key, xt_object_get(init, key));
62
+ }
63
+ }
39
64
  xt_object_set(event, xt_string_from_cstr("type"), xt_string_from_cstr(name.c_str()));
40
- xt_object_set(event, xt_string_from_cstr("target"), XT_NULL);
41
- xt_object_set(event, xt_string_from_cstr("currentTarget"), XT_NULL);
42
- xt_object_set(event, xt_string_from_cstr("defaultPrevented"), XT_FALSE);
43
- xt_value bubbles = xt_object_get_cstr(init, "bubbles");
44
- xt_object_set(event, xt_string_from_cstr("bubbles"), XT_IS_UNDEFINED(bubbles) ? XT_FALSE : bubbles);
45
- xt_value cancelable = xt_object_get_cstr(init, "cancelable");
46
- xt_object_set(event, xt_string_from_cstr("cancelable"),
47
- XT_IS_UNDEFINED(cancelable) ? XT_FALSE : cancelable);
48
- xt_value detail = xt_object_get_cstr(init, "detail");
49
- xt_object_set(event, xt_string_from_cstr("detail"),
50
- XT_IS_UNDEFINED(detail) ? XT_NULL : detail);
65
+ default_field(event, "target", XT_NULL);
66
+ default_field(event, "currentTarget", XT_NULL);
67
+ default_field(event, "defaultPrevented", XT_FALSE);
68
+ default_field(event, "bubbles", XT_FALSE);
69
+ default_field(event, "cancelable", XT_FALSE);
70
+ default_field(event, "composed", XT_FALSE);
71
+ default_field(event, "isTrusted", XT_FALSE);
72
+ default_field(event, "eventPhase", xt_number(0));
73
+ default_field(event, "detail", XT_NULL);
74
+ default_field(event, "timeStamp", xt_number((double)SDL_GetTicks()));
75
+ /* Marks an event object built here, so `dispatchEvent(ev)` can reuse it (an
76
+ * event keeps its identity through dispatch, as in the DOM). */
77
+ xt_object_set(event, xt_string_from_cstr("__xt_gui_event"), XT_TRUE);
51
78
  return event;
52
79
  }
53
80
 
@@ -64,18 +91,32 @@ void remove_listener(XtGuiWindow *win, xtgui::Node *node, const std::string &typ
64
91
  }
65
92
  }
66
93
 
94
+ /** Call one listener batch. `phase` mirrors the DOM constants (1 capture,
95
+ * 2 at target, 3 bubble) so `event.eventPhase` is observable, and a passive
96
+ * listener runs with `preventDefault()` disabled. */
67
97
  static bool call_node_listeners(XtGuiWindow *win, xtgui::Node *node, const char *type,
68
- xt_value event, bool capture) {
98
+ xt_value event, bool capture, int32_t phase) {
69
99
  auto found = win->node_listeners.find(node);
70
100
  if (found == win->node_listeners.end()) return false;
71
101
  std::vector<XtGuiNodeListener> snapshot = found->second;
102
+ xt_value previousPassive = xt_object_get_cstr(event, "__xt_gui_passive");
72
103
  for (const XtGuiNodeListener &listener : snapshot) {
73
104
  if (listener.type != type || listener.capture != capture) continue;
105
+ /* An aborted `AbortSignal` removes the listener. Drop it here so it also
106
+ * stops consuming the slot for future dispatches. */
107
+ if (xt_gui_signal_aborted(listener.signal)) {
108
+ remove_listener(win, node, listener.type, listener.fn, listener.capture);
109
+ continue;
110
+ }
74
111
  if (listener.once) remove_listener(win, node, listener.type, listener.fn, listener.capture);
75
112
  xt_value self = xt_gui_node_handle(win, node);
76
113
  xt_object_set(event, xt_string_from_cstr("currentTarget"), self);
114
+ xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number((double)phase));
115
+ xt_object_set(event, xt_string_from_cstr("__xt_gui_passive"),
116
+ listener.passive ? XT_TRUE : XT_FALSE);
77
117
  xt_value args[1] = {event};
78
118
  xt_call_with_this(listener.fn, self, 1, args);
119
+ xt_object_set(event, xt_string_from_cstr("__xt_gui_passive"), previousPassive);
79
120
  if (value_truthy(xt_object_get_cstr(event, "__stopImmediate"))) return true;
80
121
  if (value_truthy(xt_object_get_cstr(event, "__stop"))) return true;
81
122
  }
@@ -88,6 +129,9 @@ METHOD(event_prevent_default) {
88
129
  (void)env;
89
130
  (void)argc;
90
131
  (void)argv;
132
+ /* A listener registered with `{ passive: true }` promised not to cancel: the
133
+ * browser ignores the call (and warns), so the engine ignores it too. */
134
+ if (value_truthy(xt_object_get_cstr(self, "__xt_gui_passive"))) return XT_UNDEFINED;
91
135
  xt_object_set(self, xt_string_from_cstr("defaultPrevented"), XT_TRUE);
92
136
  return XT_UNDEFINED;
93
137
  }
@@ -109,6 +153,22 @@ METHOD(event_stop_immediate_propagation) {
109
153
  return XT_UNDEFINED;
110
154
  }
111
155
 
156
+ /** `event.composedPath()`: the propagation path recorded by the dispatcher,
157
+ * target first, as element handles. A detached event has an empty path. */
158
+ METHOD(event_composed_path) {
159
+ (void)env;
160
+ (void)argc;
161
+ (void)argv;
162
+ xt_value result = xt_array_new(0, nullptr);
163
+ xt_value path = xt_object_get_cstr(self, "__xt_gui_path");
164
+ if (!XT_IS_ARRAY(path)) return result;
165
+ int32_t count = (int32_t)xt_to_number(xt_array_length(path));
166
+ for (int32_t i = 0; i < count; i++) {
167
+ xt_array_push(result, xt_array_get(path, xt_number((double)i)));
168
+ }
169
+ return result;
170
+ }
171
+
112
172
  #undef METHOD
113
173
 
114
174
  } // namespace domapi
@@ -123,13 +183,29 @@ void xt_gui_emit_dom_event(XtGuiWindow *win, const char *type, const xtgui::Node
123
183
  path.push_back(node);
124
184
  }
125
185
  xt_value event = make_dom_event(win, type, target, legacy);
186
+ /* `element.dispatchEvent(ev)` hands over a real event object: dispatch that
187
+ * same object (identity is observable), only rebinding its target/path. */
188
+ if (XT_IS_OBJECT(legacy) && value_truthy(xt_object_get_cstr(legacy, "__xt_gui_event"))) {
189
+ event = legacy;
190
+ xt_object_set(event, xt_string_from_cstr("target"), xt_gui_node_handle(win, target));
191
+ xt_object_set(event, xt_string_from_cstr("currentTarget"), XT_NULL);
192
+ xt_object_set(event, xt_string_from_cstr("eventPhase"), xt_number(0));
193
+ }
194
+ xt_value handlePath = xt_array_new(0, nullptr);
195
+ for (xtgui::Node *node : path) xt_array_push(handlePath, xt_gui_node_handle(win, node));
196
+ xt_object_set(event, xt_string_from_cstr("__xt_gui_path"), handlePath);
197
+ bool bubbles = value_truthy(xt_object_get_cstr(event, "bubbles"));
126
198
  /* Capture phase: root -> target. */
127
199
  for (auto it = path.rbegin(); it != path.rend(); ++it) {
128
- if (call_node_listeners(win, *it, type, event, true)) return;
200
+ int32_t phase = *it == target ? 2 : 1;
201
+ if (call_node_listeners(win, *it, type, event, true, phase)) return;
129
202
  }
130
- /* Bubble phase: target -> root. */
203
+ /* Bubble phase: target -> root; a non-bubbling event stops after the target
204
+ * (its non-capture listeners still run there). */
131
205
  for (auto it = path.begin(); it != path.end(); ++it) {
132
- if (call_node_listeners(win, *it, type, event, false)) return;
206
+ if (!bubbles && *it != target) break;
207
+ int32_t phase = *it == target ? 2 : 3;
208
+ if (call_node_listeners(win, *it, type, event, false, phase)) return;
133
209
  }
134
210
  }
135
211
 
@@ -27,6 +27,7 @@ extern xt_value g_document_proto;
27
27
  extern xt_value g_event_proto;
28
28
  extern xt_value g_class_list_proto;
29
29
  extern xt_value g_style_proto;
30
+ extern xt_value g_computed_style_proto;
30
31
  extern xt_value g_canvas_proto;
31
32
  extern xt_value g_canvas_gradient_proto;
32
33
  extern xt_value g_webgl_proto;
@@ -49,10 +50,16 @@ xt_value document_proto();
49
50
  xt_value event_proto();
50
51
  xt_value class_list_proto();
51
52
  xt_value style_proto();
53
+ xt_value computed_style_proto();
52
54
  xt_value canvas_proto();
53
55
  xt_value canvas_gradient_proto();
54
56
  xt_value webgl_proto();
55
57
  xt_value webgl2_proto();
58
+ /** `AbortController` / `AbortSignal` prototypes and constructors. */
59
+ xt_value abort_controller_proto();
60
+ xt_value abort_signal_proto();
61
+ xt_value abort_controller_new();
62
+ xt_value abort_signal_new(bool aborted);
56
63
  /** The prototype a WebGL object of `kind` inherits (`buffer`, `texture`, …),
57
64
  * so `buffer instanceof WebGLBuffer` holds. */
58
65
  xt_value webgl_object_proto(const std::string &kind);
@@ -60,6 +67,16 @@ xt_value webgl_object_proto(const std::string &kind);
60
67
  /** Create/return the 2D context for a pending `<canvas>` handle (before load). */
61
68
  xt_value xt_canvas_pending_context(xt_value self, XtGuiWindow *win);
62
69
 
70
+ /* -- Path2D --------------------------------------------------------------- */
71
+
72
+ /** True when `value` is a `Path2D` handle. */
73
+ bool path2d_is(xt_value value);
74
+ /** The user-space subpaths a `Path2D` handle holds. */
75
+ std::vector<xtgui::CanvasSubpath> path2d_subpaths(xt_value value);
76
+ /** `new Path2D()` / `new Path2D(other)`. */
77
+ xt_value path2d_create(xt_value source);
78
+ xt_value path2d_proto();
79
+
63
80
  /* -- canvas helpers shared by `dom_api_canvas*.cpp` ----------------------- */
64
81
 
65
82
  /** The engine canvas behind a 2D context handle, or null when stale/foreign. */
@@ -77,6 +94,9 @@ uint64_t canvas_gradient_id(xt_value handle, const xtgui::Canvas2D *canvas);
77
94
 
78
95
  /** The engine WebGL context behind a context handle, or null. */
79
96
  xtgui::WebGLContext *webgl_from_context(xt_value self);
97
+ /** Like `webgl_from_context`, but null while the context is lost: every GL
98
+ * entry point except `getError`/`isContextLost` is a no-op in that state. */
99
+ xtgui::WebGLContext *webgl_live_context(xt_value self);
80
100
  /** The numeric context id stored on a context handle (0 when absent). */
81
101
  uint64_t webgl_context_id(xt_value self);
82
102
  /** Create the JavaScript handle for a canvas's WebGL context. */
@@ -95,9 +115,19 @@ std::vector<double> webgl_numbers(xt_value value);
95
115
  std::vector<uint8_t> webgl_bytes(xt_value value);
96
116
  /** Store `count` bytes into a typed array / array handle. */
97
117
  void webgl_write_bytes(xt_value target, const uint8_t *bytes, size_t count);
118
+ /** Convert a source pixel array (RGBA/RGB/LUMINANCE/…) to RGBA8. */
119
+ void webgl_convert_pixels(const uint8_t *source, int count, int format, bool premultiply,
120
+ std::vector<uint8_t> *out);
121
+ /** Decode a `texImage2D` source (`<img>`, `<canvas>`, `<video>`, `ImageData`,
122
+ * `ImageBitmap`) into top-down RGBA8. */
123
+ bool webgl_read_source(xt_value source, int *width, int *height,
124
+ std::vector<uint8_t> *rgba, bool premultiply);
98
125
  /** Read the `UNPACK_FLIP_Y_WEBGL` / `UNPACK_PREMULTIPLY_ALPHA_WEBGL` flags set
99
126
  * through `pixelStorei`. */
100
127
  void webgl_unpack_flags(xt_value context, bool *flip_y, bool *premultiply);
128
+ /** Extension object for a compressed-texture family (null when unsupported,
129
+ * undefined when the name is not one of those extensions). */
130
+ xt_value webgl_compressed_extension(const std::string &name, xtgui::WebGLContext *context);
101
131
 
102
132
  /** How a pending handle names the node it will bind to. */
103
133
  enum PendingKind { pending_by_id = 1, pending_by_selector = 2 };
@@ -144,7 +174,9 @@ METHOD(doc_create_element_ns);
144
174
  METHOD(doc_hidden_get);
145
175
  METHOD(doc_visibility_state_get);
146
176
  METHOD(doc_ready_state_get);
177
+ METHOD(doc_has_focus);
147
178
  METHOD(doc_title_get);
179
+ METHOD(doc_title_set);
148
180
  METHOD(doc_default_view_get);
149
181
  METHOD(doc_create_text_node);
150
182
  METHOD(doc_body_get);
@@ -153,6 +185,9 @@ METHOD(doc_document_element_get);
153
185
  /* node getters */
154
186
  METHOD(node_tag_name_get);
155
187
  METHOD(node_node_type_get);
188
+ METHOD(node_namespace_uri_get);
189
+ METHOD(node_local_name_get);
190
+ METHOD(node_prefix_get);
156
191
  METHOD(node_id_get);
157
192
  METHOD(node_id_set);
158
193
  METHOD(node_class_name_get);
@@ -183,6 +218,10 @@ METHOD(node_query_selector);
183
218
  METHOD(node_query_selector_all);
184
219
  METHOD(node_matches);
185
220
  METHOD(node_append_child);
221
+ METHOD(node_append);
222
+ METHOD(node_prepend);
223
+ METHOD(node_replace_children);
224
+ METHOD(node_insert_adjacent_html);
186
225
  METHOD(node_insert_before);
187
226
  METHOD(node_remove_child);
188
227
  METHOD(node_replace_child);
@@ -206,6 +245,7 @@ xt_value event_construct(xt_value type, xt_value init);
206
245
  METHOD(event_prevent_default);
207
246
  METHOD(event_stop_propagation);
208
247
  METHOD(event_stop_immediate_propagation);
248
+ METHOD(event_composed_path);
209
249
 
210
250
  /* media (`<video>`/`<audio>`) */
211
251
  METHOD(node_play);
@@ -272,11 +312,21 @@ METHOD(canvas_stroke);
272
312
  METHOD(canvas_clear_rect);
273
313
  METHOD(canvas_fill_rect);
274
314
  METHOD(canvas_stroke_rect);
275
- METHOD(canvas_measure_text);
276
- METHOD(canvas_fill_text);
315
+ METHOD(canvas_measure_text);METHOD(canvas_fill_text);
277
316
  METHOD(canvas_stroke_text);
278
317
  METHOD(canvas_draw_image);
279
318
  METHOD(canvas_get_image_data);
319
+ METHOD(canvas_get_context_attributes);
320
+ METHOD(canvas_put_image_data);
321
+ METHOD(canvas_create_image_data);
322
+ METHOD(canvas_create_pattern);
323
+ METHOD(canvas_to_data_url);
324
+ METHOD(canvas_to_blob);
325
+ /** The cached `CanvasPattern` handle for `id` on `canvas`. */
326
+ xt_value canvas_pattern_handle(xt_value context, const xtgui::Canvas2D *canvas, uint64_t id);
327
+ /** The pattern id stored on a handle, or 0 when it is not a pattern for
328
+ * `canvas` (stale or created by another canvas). */
329
+ uint64_t canvas_pattern_id(xt_value handle, const xtgui::Canvas2D *canvas);
280
330
  METHOD(canvas_ellipse);
281
331
  METHOD(canvas_round_rect);
282
332
  METHOD(canvas_line_cap_get);
@@ -303,7 +353,23 @@ METHOD(class_list_contains);
303
353
  /* style methods */
304
354
  METHOD(style_set_property);
305
355
  METHOD(style_get_property_value);
356
+ METHOD(style_get_property_priority);
306
357
  METHOD(style_remove_property);
358
+ METHOD(style_css_text_get);
359
+ METHOD(style_css_text_set);
360
+ METHOD(style_length_get);
361
+ METHOD(style_item);
362
+ /** Install the dynamic named-property hooks on a `style` handle
363
+ * (`style.width = "50%"`). */
364
+ void install_style_hooks(xt_value style);
365
+
366
+ /* `dataset` (a DOMStringMap over `data-*` attributes) and computed style */
367
+ METHOD(node_dataset_get);
368
+ METHOD(computed_style_get_property_value);
369
+ METHOD(computed_style_get_property_priority);
370
+ /** Build the computed-style handle for an element (and optional pseudo). */
371
+ xt_value computed_style_handle(xt_value element, xt_value pseudo);
372
+ xt_value computed_style_proto();
307
373
 
308
374
  /* WebGL context: state and queries */
309
375
  METHOD(webgl_canvas_get);
@@ -426,6 +492,8 @@ METHOD(webgl_tex_parameterf);
426
492
  METHOD(webgl_get_tex_parameter);
427
493
  METHOD(webgl_tex_image_2d);
428
494
  METHOD(webgl_tex_sub_image_2d);
495
+ METHOD(webgl_compressed_tex_image_2d);
496
+ METHOD(webgl_compressed_tex_sub_image_2d);
429
497
  METHOD(webgl_generate_mipmap);
430
498
  METHOD(webgl_create_framebuffer);
431
499
  METHOD(webgl_delete_framebuffer);
@@ -495,6 +563,7 @@ METHOD(webgl2_get_uniform_block_index);
495
563
  METHOD(webgl2_get_active_uniform_block_parameter);
496
564
  METHOD(webgl2_get_active_uniform_block_name);
497
565
  METHOD(webgl2_uniform_block_binding);
566
+ METHOD(webgl2_drawing_buffer_storage);
498
567
  METHOD(webgl2_tex_storage_2d);
499
568
  METHOD(webgl2_tex_storage_3d);
500
569
  METHOD(webgl2_tex_image_3d);
@@ -0,0 +1,159 @@
1
+ /*
2
+ * xbintsc GUI engine — the parent/child convenience mutators.
3
+ *
4
+ * `append`, `prepend`, `replaceChildren` and `insertAdjacentHTML` are the APIs
5
+ * modern pages actually use to build DOM (they accept nodes and plain strings
6
+ * in one call); the engine previously only had `appendChild`/`innerHTML`.
7
+ * Strings become text nodes, as in the DOM.
8
+ */
9
+
10
+ #include "dom_api_internal.h"
11
+
12
+ #include <memory>
13
+ #include <string>
14
+ #include <vector>
15
+
16
+ namespace domapi {
17
+
18
+ namespace {
19
+
20
+ /** A node handle, or a new text node for a string argument. */
21
+ xtgui::Node *argument_node(XtGuiWindow *win, xt_value value) {
22
+ xtgui::Node *node = resolve_node(value, nullptr);
23
+ if (node != nullptr) return node;
24
+ if (XT_IS_UNDEFINED(value) || value == XT_NULL) return nullptr;
25
+ return win->document->createTextNode(to_string_value(value));
26
+ }
27
+
28
+ void insert_argument(XtGuiWindow *win, xtgui::Node *parent, xt_value value, xtgui::Node *before) {
29
+ xtgui::Node *node = argument_node(win, value);
30
+ if (node == nullptr) return;
31
+ if (before != nullptr) {
32
+ win->document->insertBefore(parent, node, before);
33
+ } else {
34
+ win->document->appendChild(parent, node);
35
+ }
36
+ }
37
+
38
+ /** Copy a parsed fragment's children into `dest`, inserting before `before`
39
+ * (nullptr appends). The parser's nodes belong to a standalone tree, so they are
40
+ * recreated through the document rather than adopted. */
41
+ void import_children(XtGuiWindow *win, xtgui::Node *dest, const xtgui::Node *parsed,
42
+ xtgui::Node *before) {
43
+ for (const std::unique_ptr<xtgui::Node> &child : parsed->children) {
44
+ xtgui::Node *copy = nullptr;
45
+ if (child->isText()) {
46
+ copy = win->document->createTextNode(child->text);
47
+ } else {
48
+ copy = win->document->createElement(child->tag);
49
+ if (copy == nullptr) continue;
50
+ for (const xtgui::Attr &attr : child->attrs) copy->attrs.push_back(attr);
51
+ import_children(win, copy, child.get(), nullptr);
52
+ }
53
+ if (copy == nullptr) continue;
54
+ if (before != nullptr) {
55
+ win->document->insertBefore(dest, copy, before);
56
+ } else {
57
+ win->document->appendChild(dest, copy);
58
+ }
59
+ }
60
+ }
61
+
62
+ xtgui::Node *next_sibling(xtgui::Node *parent, xtgui::Node *node) {
63
+ if (parent == nullptr) return nullptr;
64
+ for (size_t i = 0; i + 1 < parent->children.size(); i++) {
65
+ if (parent->children[i].get() == node) return parent->children[i + 1].get();
66
+ }
67
+ return nullptr;
68
+ }
69
+
70
+ xtgui::Node *first_child(xtgui::Node *node) {
71
+ return node->children.empty() ? nullptr : node->children.front().get();
72
+ }
73
+
74
+ } // namespace
75
+
76
+ #define METHOD(name) xt_value name(xt_value self, xt_value env, int32_t argc, xt_value *argv)
77
+
78
+ METHOD(node_append) {
79
+ (void)env;
80
+ XtGuiWindow *win = nullptr;
81
+ xtgui::Node *node = resolve_node(self, &win);
82
+ if (node == nullptr || win == nullptr) return XT_UNDEFINED;
83
+ for (int32_t i = 0; i < argc; i++) insert_argument(win, node, xt_arg(argc, argv, i), nullptr);
84
+ mark_dirty(win);
85
+ return XT_UNDEFINED;
86
+ }
87
+
88
+ METHOD(node_prepend) {
89
+ (void)env;
90
+ XtGuiWindow *win = nullptr;
91
+ xtgui::Node *node = resolve_node(self, &win);
92
+ if (node == nullptr || win == nullptr) return XT_UNDEFINED;
93
+ /* The reference stays the original first child; inserting each argument before
94
+ * it keeps the argument order. */
95
+ xtgui::Node *reference = first_child(node);
96
+ for (int32_t i = 0; i < argc; i++) {
97
+ insert_argument(win, node, xt_arg(argc, argv, i), reference);
98
+ }
99
+ mark_dirty(win);
100
+ return XT_UNDEFINED;
101
+ }
102
+
103
+ METHOD(node_replace_children) {
104
+ (void)env;
105
+ XtGuiWindow *win = nullptr;
106
+ xtgui::Node *node = resolve_node(self, &win);
107
+ if (node == nullptr || win == nullptr || win->document == nullptr) return XT_UNDEFINED;
108
+ /* Detach node arguments first: one of them may be a current child that the
109
+ * replacement keeps. */
110
+ for (int32_t i = 0; i < argc; i++) {
111
+ xtgui::Node *child = resolve_node(xt_arg(argc, argv, i), nullptr);
112
+ if (child == nullptr) continue;
113
+ if (child->parent == node) win->document->removeChild(node, child);
114
+ }
115
+ std::vector<xtgui::Node *> existing;
116
+ for (const std::unique_ptr<xtgui::Node> &child : node->children) {
117
+ existing.push_back(child.get());
118
+ }
119
+ for (xtgui::Node *child : existing) {
120
+ win->document->removeChild(node, child);
121
+ discard_subtree(win, child);
122
+ }
123
+ for (int32_t i = 0; i < argc; i++) insert_argument(win, node, xt_arg(argc, argv, i), nullptr);
124
+ mark_dirty(win);
125
+ return XT_UNDEFINED;
126
+ }
127
+
128
+ /** `insertAdjacentHTML(position, html)`; `position` is beforebegin,
129
+ * afterbegin, beforeend or afterend. Scripts in the fragment do not run. */
130
+ METHOD(node_insert_adjacent_html) {
131
+ (void)env;
132
+ XtGuiWindow *win = nullptr;
133
+ xtgui::Node *node = resolve_node(self, &win);
134
+ if (node == nullptr || win == nullptr || win->document == nullptr || argc < 2) {
135
+ return XT_UNDEFINED;
136
+ }
137
+ std::string position = to_string_value(xt_arg(argc, argv, 0));
138
+ std::string html = to_string_value(xt_arg(argc, argv, 1));
139
+ std::unique_ptr<xtgui::Node> parsed = xtgui::xt_html_parse(html);
140
+ if (!parsed) return XT_UNDEFINED;
141
+ if (position == "beforebegin" || position == "afterend") {
142
+ xtgui::Node *parent = node->parent;
143
+ if (parent == nullptr || parent->isDocument()) return XT_UNDEFINED;
144
+ xtgui::Node *reference = position == "beforebegin" ? node : next_sibling(parent, node);
145
+ import_children(win, parent, parsed.get(), reference);
146
+ } else if (position == "afterbegin") {
147
+ import_children(win, node, parsed.get(), first_child(node));
148
+ } else if (position == "beforeend") {
149
+ import_children(win, node, parsed.get(), nullptr);
150
+ } else {
151
+ return XT_UNDEFINED;
152
+ }
153
+ mark_dirty(win);
154
+ return XT_UNDEFINED;
155
+ }
156
+
157
+ #undef METHOD
158
+
159
+ } // namespace domapi