@salve-software/react-native-nitro-jsdom 1.0.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.
- package/LICENSE +21 -0
- package/NitroJsdom.podspec +63 -0
- package/README.md +95 -0
- package/android/CMakeLists.txt +86 -0
- package/android/build.gradle +148 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +9 -0
- package/android/src/main/java/com/nitrojsdom/NitroJsdomPackage.kt +20 -0
- package/cpp/HybridHtmlSandbox.cpp +171 -0
- package/cpp/HybridHtmlSandbox.hpp +47 -0
- package/cpp/lexbor/LexborDocument.cpp +365 -0
- package/cpp/lexbor/LexborDocument.hpp +71 -0
- package/cpp/quickjs/DOMBindings.cpp +57 -0
- package/cpp/quickjs/DOMBindings.hpp +21 -0
- package/cpp/quickjs/DOMBindingsInternal.cpp +157 -0
- package/cpp/quickjs/DOMBindingsInternal.hpp +83 -0
- package/cpp/quickjs/MutationObservers.cpp +546 -0
- package/cpp/quickjs/MutationObservers.hpp +127 -0
- package/cpp/quickjs/QuickJSRuntime.cpp +475 -0
- package/cpp/quickjs/QuickJSRuntime.hpp +110 -0
- package/cpp/quickjs/Storage.cpp +130 -0
- package/cpp/quickjs/Storage.hpp +40 -0
- package/cpp/quickjs/bindings/ClassListBindings.cpp +241 -0
- package/cpp/quickjs/bindings/ClassListBindings.hpp +29 -0
- package/cpp/quickjs/bindings/DatasetBindings.cpp +120 -0
- package/cpp/quickjs/bindings/DatasetBindings.hpp +20 -0
- package/cpp/quickjs/bindings/DocumentBindings.cpp +155 -0
- package/cpp/quickjs/bindings/DocumentBindings.hpp +19 -0
- package/cpp/quickjs/bindings/ElementBindings.cpp +1106 -0
- package/cpp/quickjs/bindings/ElementBindings.hpp +19 -0
- package/cpp/quickjs/bindings/EventBindings.cpp +352 -0
- package/cpp/quickjs/bindings/EventBindings.hpp +21 -0
- package/cpp/quickjs/bindings/FetchBindings.cpp +347 -0
- package/cpp/quickjs/bindings/FetchBindings.hpp +16 -0
- package/cpp/quickjs/bindings/StyleBindings.cpp +245 -0
- package/cpp/quickjs/bindings/StyleBindings.hpp +22 -0
- package/cpp/quickjs/bindings/TimerBindings.cpp +94 -0
- package/cpp/quickjs/bindings/TimerBindings.hpp +16 -0
- package/cpp/quickjs/bindings/WindowBindings.cpp +211 -0
- package/cpp/quickjs/bindings/WindowBindings.hpp +16 -0
- package/ios/Bridge.h +8 -0
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js +138 -0
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/index.js +17 -0
- package/lib/commonjs/classes/JSDOM/index.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/IFetchOptions.js +2 -0
- package/lib/commonjs/classes/JSDOM/types/IFetchOptions.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/IJSDOMOptions.js +6 -0
- package/lib/commonjs/classes/JSDOM/types/IJSDOMOptions.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/index.js +2 -0
- package/lib/commonjs/classes/JSDOM/types/index.js.map +1 -0
- package/lib/commonjs/classes/index.js +17 -0
- package/lib/commonjs/classes/index.js.map +1 -0
- package/lib/commonjs/index.js +17 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/HtmlSandbox.nitro.js +6 -0
- package/lib/commonjs/specs/HtmlSandbox.nitro.js.map +1 -0
- package/lib/module/classes/JSDOM/JSDOM.class.js +133 -0
- package/lib/module/classes/JSDOM/JSDOM.class.js.map +1 -0
- package/lib/module/classes/JSDOM/index.js +4 -0
- package/lib/module/classes/JSDOM/index.js.map +1 -0
- package/lib/module/classes/JSDOM/types/IFetchOptions.js +2 -0
- package/lib/module/classes/JSDOM/types/IFetchOptions.js.map +1 -0
- package/lib/module/classes/JSDOM/types/IJSDOMOptions.js +4 -0
- package/lib/module/classes/JSDOM/types/IJSDOMOptions.js.map +1 -0
- package/lib/module/classes/JSDOM/types/index.js +2 -0
- package/lib/module/classes/JSDOM/types/index.js.map +1 -0
- package/lib/module/classes/index.js +4 -0
- package/lib/module/classes/index.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/HtmlSandbox.nitro.js +4 -0
- package/lib/module/specs/HtmlSandbox.nitro.js.map +1 -0
- package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts +74 -0
- package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/index.d.ts +3 -0
- package/lib/typescript/src/classes/JSDOM/index.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/IFetchOptions.d.ts +14 -0
- package/lib/typescript/src/classes/JSDOM/types/IFetchOptions.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts +57 -0
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/index.d.ts +3 -0
- package/lib/typescript/src/classes/JSDOM/types/index.d.ts.map +1 -0
- package/lib/typescript/src/classes/index.d.ts +2 -0
- package/lib/typescript/src/classes/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts +13 -0
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts.map +1 -0
- package/nitro.json +26 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroJsdom+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroJsdom+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroJsdomOnLoad.cpp +49 -0
- package/nitrogen/generated/android/NitroJsdomOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrojsdom/NitroJsdomOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NitroJsdom+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NitroJsdomAutolinking.mm +35 -0
- package/nitrogen/generated/ios/NitroJsdomAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.cpp +26 -0
- package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.hpp +73 -0
- package/package.json +131 -0
- package/src/classes/JSDOM/JSDOM.class.ts +145 -0
- package/src/classes/JSDOM/index.ts +2 -0
- package/src/classes/JSDOM/types/IFetchOptions.ts +14 -0
- package/src/classes/JSDOM/types/IJSDOMOptions.ts +57 -0
- package/src/classes/JSDOM/types/index.ts +2 -0
- package/src/classes/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/specs/HtmlSandbox.nitro.ts +33 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Registers the "Element" JS class and its prototype: attributes, classList,
|
|
4
|
+
// dataset, style, generic Node traversal (nodeType/nodeName/childNodes/...),
|
|
5
|
+
// and DOM mutation/query methods (appendChild, cloneNode, querySelector, ...).
|
|
6
|
+
//
|
|
7
|
+
// addEventListener/removeEventListener/dispatchEvent are attached separately
|
|
8
|
+
// by EventBindings::install(), which must run after this one.
|
|
9
|
+
|
|
10
|
+
#include "quickjs.h"
|
|
11
|
+
|
|
12
|
+
namespace margelo::nitro::nitrojsdom {
|
|
13
|
+
|
|
14
|
+
class ElementBindings {
|
|
15
|
+
public:
|
|
16
|
+
static void install(JSContext* ctx);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#include "EventBindings.hpp"
|
|
2
|
+
#include "../DOMBindingsInternal.hpp"
|
|
3
|
+
#include "../QuickJSRuntime.hpp"
|
|
4
|
+
#include "../../lexbor/LexborDocument.hpp"
|
|
5
|
+
#include <lexbor/html/html.h>
|
|
6
|
+
#include <lexbor/dom/dom.h>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace margelo::nitro::nitrojsdom {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
JSClassID js_event_class_id = 0;
|
|
15
|
+
JSClassDef js_event_class = { "Event", .finalizer = nullptr };
|
|
16
|
+
|
|
17
|
+
// ── Event / CustomEvent constructors ──────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
JSValue js_Event_constructor(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
20
|
+
JSValue obj = JS_NewObjectClass(ctx, js_event_class_id);
|
|
21
|
+
const char* type_str = (argc >= 1) ? JS_ToCString(ctx, argv[0]) : nullptr;
|
|
22
|
+
JS_SetPropertyStr(ctx, obj, "type", JS_NewString(ctx, type_str ? type_str : ""));
|
|
23
|
+
if (type_str) JS_FreeCString(ctx, type_str);
|
|
24
|
+
|
|
25
|
+
bool bubbles = false;
|
|
26
|
+
bool cancelable = false;
|
|
27
|
+
if (argc >= 2 && JS_IsObject(argv[1])) {
|
|
28
|
+
JSValue b = JS_GetPropertyStr(ctx, argv[1], "bubbles");
|
|
29
|
+
if (JS_IsException(b)) { JS_FreeValue(ctx, JS_GetException(ctx)); }
|
|
30
|
+
else { bubbles = JS_ToBool(ctx, b) > 0; JS_FreeValue(ctx, b); }
|
|
31
|
+
|
|
32
|
+
JSValue c = JS_GetPropertyStr(ctx, argv[1], "cancelable");
|
|
33
|
+
if (JS_IsException(c)) { JS_FreeValue(ctx, JS_GetException(ctx)); }
|
|
34
|
+
else { cancelable = JS_ToBool(ctx, c) > 0; JS_FreeValue(ctx, c); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
JS_SetPropertyStr(ctx, obj, "defaultPrevented", JS_NewBool(ctx, false));
|
|
38
|
+
JS_SetPropertyStr(ctx, obj, "bubbles", JS_NewBool(ctx, bubbles));
|
|
39
|
+
JS_SetPropertyStr(ctx, obj, "cancelable", JS_NewBool(ctx, cancelable));
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
JSValue js_CustomEvent_constructor(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
44
|
+
JSValue obj = JS_NewObjectClass(ctx, js_event_class_id);
|
|
45
|
+
const char* type_str = (argc >= 1) ? JS_ToCString(ctx, argv[0]) : nullptr;
|
|
46
|
+
JS_SetPropertyStr(ctx, obj, "type", JS_NewString(ctx, type_str ? type_str : ""));
|
|
47
|
+
if (type_str) JS_FreeCString(ctx, type_str);
|
|
48
|
+
|
|
49
|
+
JSValue detail = JS_UNDEFINED;
|
|
50
|
+
bool bubbles = false;
|
|
51
|
+
bool cancelable = false;
|
|
52
|
+
|
|
53
|
+
if (argc >= 2 && JS_IsObject(argv[1])) {
|
|
54
|
+
JSValue d = JS_GetPropertyStr(ctx, argv[1], "detail");
|
|
55
|
+
if (JS_IsException(d)) { JS_FreeValue(ctx, JS_GetException(ctx)); }
|
|
56
|
+
else { detail = d; }
|
|
57
|
+
|
|
58
|
+
JSValue b = JS_GetPropertyStr(ctx, argv[1], "bubbles");
|
|
59
|
+
if (JS_IsException(b)) { JS_FreeValue(ctx, JS_GetException(ctx)); }
|
|
60
|
+
else { bubbles = JS_ToBool(ctx, b) > 0; JS_FreeValue(ctx, b); }
|
|
61
|
+
|
|
62
|
+
JSValue c = JS_GetPropertyStr(ctx, argv[1], "cancelable");
|
|
63
|
+
if (JS_IsException(c)) { JS_FreeValue(ctx, JS_GetException(ctx)); }
|
|
64
|
+
else { cancelable = JS_ToBool(ctx, c) > 0; JS_FreeValue(ctx, c); }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
JS_SetPropertyStr(ctx, obj, "detail", detail);
|
|
68
|
+
JS_SetPropertyStr(ctx, obj, "bubbles", JS_NewBool(ctx, bubbles));
|
|
69
|
+
JS_SetPropertyStr(ctx, obj, "cancelable", JS_NewBool(ctx, cancelable));
|
|
70
|
+
JS_SetPropertyStr(ctx, obj, "defaultPrevented", JS_NewBool(ctx, false));
|
|
71
|
+
return obj;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Event prototype methods ───────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
JSValue js_event_preventDefault(JSContext* ctx, JSValue this_val, int, JSValue*) {
|
|
77
|
+
if (get_bool_prop(ctx, this_val, "cancelable"))
|
|
78
|
+
JS_SetPropertyStr(ctx, this_val, "defaultPrevented", JS_NewBool(ctx, true));
|
|
79
|
+
return JS_UNDEFINED;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
JSValue js_event_stopPropagation(JSContext* ctx, JSValue this_val, int, JSValue*) {
|
|
83
|
+
JS_SetPropertyStr(ctx, this_val, "__propagationStopped", JS_NewBool(ctx, true));
|
|
84
|
+
return JS_UNDEFINED;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
JSValue js_event_stopImmediatePropagation(JSContext* ctx, JSValue this_val, int, JSValue*) {
|
|
88
|
+
JS_SetPropertyStr(ctx, this_val, "__propagationStopped", JS_NewBool(ctx, true));
|
|
89
|
+
JS_SetPropertyStr(ctx, this_val, "__immediatePropagationStopped", JS_NewBool(ctx, true));
|
|
90
|
+
return JS_UNDEFINED;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── addEventListener / removeEventListener (element) ──────────────────────────
|
|
94
|
+
|
|
95
|
+
JSValue js_el_addEventListener(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
96
|
+
auto* rctx = get_ctx(ctx);
|
|
97
|
+
if (!rctx || argc < 2 || !JS_IsFunction(ctx, argv[1])) return JS_UNDEFINED;
|
|
98
|
+
auto* node_ptr = unwrap_node(ctx, this_val);
|
|
99
|
+
if (!node_ptr) return JS_UNDEFINED;
|
|
100
|
+
auto* el = reinterpret_cast<lxb_dom_element_t*>(node_ptr);
|
|
101
|
+
|
|
102
|
+
const char* type_str = JS_ToCString(ctx, argv[0]);
|
|
103
|
+
if (!type_str) return JS_UNDEFINED;
|
|
104
|
+
std::string event_type(type_str);
|
|
105
|
+
JS_FreeCString(ctx, type_str);
|
|
106
|
+
|
|
107
|
+
void* node = lxb_dom_interface_node(el);
|
|
108
|
+
for (auto& l : rctx->listeners) {
|
|
109
|
+
if (l.node == node && l.event_type == event_type) {
|
|
110
|
+
JSValue* stored = static_cast<JSValue*>(l.callback);
|
|
111
|
+
if (JS_VALUE_GET_TAG(*stored) == JS_VALUE_GET_TAG(argv[1]) &&
|
|
112
|
+
JS_VALUE_GET_PTR(*stored) == JS_VALUE_GET_PTR(argv[1]))
|
|
113
|
+
return JS_UNDEFINED;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
EventListener listener;
|
|
118
|
+
listener.node = node;
|
|
119
|
+
listener.event_type = event_type;
|
|
120
|
+
listener.callback = new JSValue(JS_DupValue(ctx, argv[1]));
|
|
121
|
+
rctx->listeners.push_back(std::move(listener));
|
|
122
|
+
return JS_UNDEFINED;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
JSValue js_el_removeEventListener(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
126
|
+
auto* rctx = get_ctx(ctx);
|
|
127
|
+
if (!rctx || argc < 2 || !JS_IsFunction(ctx, argv[1])) return JS_UNDEFINED;
|
|
128
|
+
auto* node_ptr = unwrap_node(ctx, this_val);
|
|
129
|
+
if (!node_ptr) return JS_UNDEFINED;
|
|
130
|
+
auto* el = reinterpret_cast<lxb_dom_element_t*>(node_ptr);
|
|
131
|
+
|
|
132
|
+
const char* type_str = JS_ToCString(ctx, argv[0]);
|
|
133
|
+
if (!type_str) return JS_UNDEFINED;
|
|
134
|
+
std::string event_type(type_str);
|
|
135
|
+
JS_FreeCString(ctx, type_str);
|
|
136
|
+
|
|
137
|
+
void* node = lxb_dom_interface_node(el);
|
|
138
|
+
for (auto it = rctx->listeners.begin(); it != rctx->listeners.end(); ) {
|
|
139
|
+
if (it->node == node && it->event_type == event_type) {
|
|
140
|
+
JSValue* stored_cb = static_cast<JSValue*>(it->callback);
|
|
141
|
+
if (JS_VALUE_GET_TAG(*stored_cb) == JS_VALUE_GET_TAG(argv[1]) &&
|
|
142
|
+
JS_VALUE_GET_PTR(*stored_cb) == JS_VALUE_GET_PTR(argv[1])) {
|
|
143
|
+
JS_FreeValue(ctx, *stored_cb);
|
|
144
|
+
delete stored_cb;
|
|
145
|
+
it = rctx->listeners.erase(it);
|
|
146
|
+
break; // remove first matching listener only (DOM spec)
|
|
147
|
+
} else {
|
|
148
|
+
++it;
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
++it;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return JS_UNDEFINED;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── dispatchEvent (shared target+bubbling logic) ──────────────────────────────
|
|
158
|
+
|
|
159
|
+
// Dispatches `event_obj` at `target_node`, walking up ancestors when
|
|
160
|
+
// event.bubbles is true. Honors stopPropagation()/stopImmediatePropagation()
|
|
161
|
+
// set by listeners via the Event prototype methods above. Returns JS_FALSE
|
|
162
|
+
// when a cancelable event had preventDefault() called, JS_TRUE otherwise
|
|
163
|
+
// (matching EventTarget.dispatchEvent's return value), or JS_EXCEPTION if a
|
|
164
|
+
// listener threw.
|
|
165
|
+
JSValue dispatch_event_on_target(JSContext* ctx, RuntimeContext* rctx, JSValue event_obj, lxb_dom_node_t* target_node) {
|
|
166
|
+
JSValue type_val = JS_GetPropertyStr(ctx, event_obj, "type");
|
|
167
|
+
const char* type_str = JS_ToCString(ctx, type_val);
|
|
168
|
+
JS_FreeValue(ctx, type_val);
|
|
169
|
+
if (!type_str) return JS_TRUE;
|
|
170
|
+
std::string event_type(type_str);
|
|
171
|
+
JS_FreeCString(ctx, type_str);
|
|
172
|
+
|
|
173
|
+
bool bubbles = get_bool_prop(ctx, event_obj, "bubbles");
|
|
174
|
+
|
|
175
|
+
lxb_dom_node_t* doc_node = nullptr;
|
|
176
|
+
if (rctx->document) {
|
|
177
|
+
doc_node = lxb_dom_interface_node(lxb_dom_interface_document(
|
|
178
|
+
static_cast<lxb_html_document_t*>(rctx->document->documentHtmlPtr())));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
JS_SetPropertyStr(ctx, event_obj, "target", make_element(ctx, target_node));
|
|
182
|
+
JS_SetPropertyStr(ctx, event_obj, "__propagationStopped", JS_NewBool(ctx, false));
|
|
183
|
+
|
|
184
|
+
std::vector<lxb_dom_node_t*> chain;
|
|
185
|
+
chain.push_back(target_node);
|
|
186
|
+
if (bubbles) {
|
|
187
|
+
for (lxb_dom_node_t* p = target_node->parent; p; p = p->parent) chain.push_back(p);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (lxb_dom_node_t* level : chain) {
|
|
191
|
+
if (get_bool_prop(ctx, event_obj, "__propagationStopped")) break;
|
|
192
|
+
|
|
193
|
+
JSValue current_target;
|
|
194
|
+
if (level == doc_node) {
|
|
195
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
196
|
+
current_target = JS_GetPropertyStr(ctx, global, "document");
|
|
197
|
+
JS_FreeValue(ctx, global);
|
|
198
|
+
} else {
|
|
199
|
+
current_target = make_element(ctx, level);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
JS_SetPropertyStr(ctx, event_obj, "currentTarget", JS_DupValue(ctx, current_target));
|
|
203
|
+
JS_SetPropertyStr(ctx, event_obj, "__immediatePropagationStopped", JS_NewBool(ctx, false));
|
|
204
|
+
|
|
205
|
+
std::vector<JSValue> cbs_to_fire;
|
|
206
|
+
std::vector<JSValue*> cb_ptrs;
|
|
207
|
+
for (auto& listener : rctx->listeners) {
|
|
208
|
+
if (listener.node == level && listener.event_type == event_type) {
|
|
209
|
+
JSValue* cb = static_cast<JSValue*>(listener.callback);
|
|
210
|
+
cbs_to_fire.push_back(JS_DupValue(ctx, *cb));
|
|
211
|
+
cb_ptrs.push_back(cb);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (size_t i = 0; i < cbs_to_fire.size(); i++) {
|
|
216
|
+
if (get_bool_prop(ctx, event_obj, "__immediatePropagationStopped")) {
|
|
217
|
+
JS_FreeValue(ctx, cbs_to_fire[i]);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
bool still_registered = false;
|
|
221
|
+
for (auto& l : rctx->listeners) {
|
|
222
|
+
if (l.node == level && l.event_type == event_type) {
|
|
223
|
+
JSValue* stored = static_cast<JSValue*>(l.callback);
|
|
224
|
+
if (JS_VALUE_GET_PTR(*stored) == JS_VALUE_GET_PTR(cbs_to_fire[i])) {
|
|
225
|
+
still_registered = true;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!still_registered) {
|
|
231
|
+
JS_FreeValue(ctx, cbs_to_fire[i]);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
JSValue ret = JS_Call(ctx, cbs_to_fire[i], current_target, 1, &event_obj);
|
|
235
|
+
JS_FreeValue(ctx, cbs_to_fire[i]);
|
|
236
|
+
if (JS_IsException(ret)) {
|
|
237
|
+
JS_FreeValue(ctx, ret);
|
|
238
|
+
for (size_t j = i + 1; j < cbs_to_fire.size(); j++) JS_FreeValue(ctx, cbs_to_fire[j]);
|
|
239
|
+
JS_FreeValue(ctx, current_target);
|
|
240
|
+
|
|
241
|
+
JSValue ex = JS_GetException(ctx);
|
|
242
|
+
std::string err = "Event callback error";
|
|
243
|
+
JSValue msg_prop = JS_GetPropertyStr(ctx, ex, "message");
|
|
244
|
+
if (!JS_IsException(msg_prop) && !JS_IsUndefined(msg_prop)) {
|
|
245
|
+
const char* s = JS_ToCString(ctx, msg_prop);
|
|
246
|
+
if (s) { err = s; JS_FreeCString(ctx, s); }
|
|
247
|
+
}
|
|
248
|
+
JS_FreeValue(ctx, msg_prop);
|
|
249
|
+
JS_FreeValue(ctx, ex);
|
|
250
|
+
JS_ThrowInternalError(ctx, "%s", err.c_str());
|
|
251
|
+
return JS_EXCEPTION;
|
|
252
|
+
}
|
|
253
|
+
JS_FreeValue(ctx, ret);
|
|
254
|
+
}
|
|
255
|
+
JS_FreeValue(ctx, current_target);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
bool defaultPrevented = get_bool_prop(ctx, event_obj, "defaultPrevented");
|
|
259
|
+
return JS_NewBool(ctx, !defaultPrevented);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
JSValue js_el_dispatchEvent(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
263
|
+
auto* rctx = get_ctx(ctx);
|
|
264
|
+
if (!rctx || argc < 1) return JS_TRUE;
|
|
265
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
266
|
+
if (!node) return JS_TRUE;
|
|
267
|
+
return dispatch_event_on_target(ctx, rctx, argv[0], node);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── document.addEventListener / dispatchEvent ─────────────────────────────────
|
|
271
|
+
|
|
272
|
+
JSValue js_doc_addEventListener(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
273
|
+
auto* rctx = get_ctx(ctx);
|
|
274
|
+
if (!rctx || argc < 2 || !JS_IsFunction(ctx, argv[1])) return JS_UNDEFINED;
|
|
275
|
+
if (!rctx->document) return JS_UNDEFINED;
|
|
276
|
+
|
|
277
|
+
const char* type_str = JS_ToCString(ctx, argv[0]);
|
|
278
|
+
if (!type_str) return JS_UNDEFINED;
|
|
279
|
+
std::string event_type(type_str);
|
|
280
|
+
JS_FreeCString(ctx, type_str);
|
|
281
|
+
|
|
282
|
+
void* doc_node = lxb_dom_interface_node(
|
|
283
|
+
lxb_dom_interface_document(
|
|
284
|
+
static_cast<lxb_html_document_t*>(rctx->document->documentHtmlPtr())));
|
|
285
|
+
|
|
286
|
+
for (auto& l : rctx->listeners) {
|
|
287
|
+
if (l.node == doc_node && l.event_type == event_type) {
|
|
288
|
+
JSValue* stored = static_cast<JSValue*>(l.callback);
|
|
289
|
+
if (JS_VALUE_GET_TAG(*stored) == JS_VALUE_GET_TAG(argv[1]) &&
|
|
290
|
+
JS_VALUE_GET_PTR(*stored) == JS_VALUE_GET_PTR(argv[1]))
|
|
291
|
+
return JS_UNDEFINED;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
EventListener listener;
|
|
296
|
+
listener.node = doc_node;
|
|
297
|
+
listener.event_type = event_type;
|
|
298
|
+
listener.callback = new JSValue(JS_DupValue(ctx, argv[1]));
|
|
299
|
+
rctx->listeners.push_back(std::move(listener));
|
|
300
|
+
return JS_UNDEFINED;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
JSValue js_doc_dispatchEvent(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
304
|
+
auto* rctx = get_ctx(ctx);
|
|
305
|
+
if (!rctx || argc < 1) return JS_TRUE;
|
|
306
|
+
if (!rctx->document) return JS_TRUE;
|
|
307
|
+
|
|
308
|
+
lxb_dom_node_t* node = lxb_dom_interface_node(
|
|
309
|
+
lxb_dom_interface_document(
|
|
310
|
+
static_cast<lxb_html_document_t*>(rctx->document->documentHtmlPtr())));
|
|
311
|
+
return dispatch_event_on_target(ctx, rctx, argv[0], node);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
} // namespace
|
|
315
|
+
|
|
316
|
+
void EventBindings::install(JSContext* ctx) {
|
|
317
|
+
if (js_event_class_id == 0) JS_NewClassID(&js_event_class_id);
|
|
318
|
+
JS_NewClass(JS_GetRuntime(ctx), js_event_class_id, &js_event_class);
|
|
319
|
+
|
|
320
|
+
// ── Event prototype ────────────────────────────────────────────────────────
|
|
321
|
+
JSValue event_proto = JS_NewObject(ctx);
|
|
322
|
+
JS_SetPropertyStr(ctx, event_proto, "preventDefault", JS_NewCFunction(ctx, js_event_preventDefault, "preventDefault", 0));
|
|
323
|
+
JS_SetPropertyStr(ctx, event_proto, "stopPropagation", JS_NewCFunction(ctx, js_event_stopPropagation, "stopPropagation", 0));
|
|
324
|
+
JS_SetPropertyStr(ctx, event_proto, "stopImmediatePropagation", JS_NewCFunction(ctx, js_event_stopImmediatePropagation, "stopImmediatePropagation", 0));
|
|
325
|
+
JS_SetClassProto(ctx, js_event_class_id, event_proto);
|
|
326
|
+
|
|
327
|
+
// ── Event / CustomEvent constructors ───────────────────────────────────────
|
|
328
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
329
|
+
JS_SetPropertyStr(ctx, global, "Event",
|
|
330
|
+
JS_NewCFunction2(ctx, js_Event_constructor, "Event", 1, JS_CFUNC_constructor, 0));
|
|
331
|
+
JS_SetPropertyStr(ctx, global, "CustomEvent",
|
|
332
|
+
JS_NewCFunction2(ctx, js_CustomEvent_constructor, "CustomEvent", 2, JS_CFUNC_constructor, 0));
|
|
333
|
+
|
|
334
|
+
// ── addEventListener/removeEventListener/dispatchEvent on Node + Element ──
|
|
335
|
+
// Added to the Node proto so text/comment nodes can also receive events.
|
|
336
|
+
JSValue node_proto = JS_GetClassProto(ctx, js_node_class_id);
|
|
337
|
+
JS_SetPropertyStr(ctx, node_proto, "addEventListener", JS_NewCFunction(ctx, js_el_addEventListener, "addEventListener", 2));
|
|
338
|
+
JS_SetPropertyStr(ctx, node_proto, "removeEventListener", JS_NewCFunction(ctx, js_el_removeEventListener, "removeEventListener", 2));
|
|
339
|
+
JS_SetPropertyStr(ctx, node_proto, "dispatchEvent", JS_NewCFunction(ctx, js_el_dispatchEvent, "dispatchEvent", 1));
|
|
340
|
+
JS_FreeValue(ctx, node_proto);
|
|
341
|
+
|
|
342
|
+
// ── addEventListener/dispatchEvent on document ─────────────────────────────
|
|
343
|
+
// DocumentBindings::install() must have already run so globalThis.document exists.
|
|
344
|
+
JSValue doc = JS_GetPropertyStr(ctx, global, "document");
|
|
345
|
+
JS_SetPropertyStr(ctx, doc, "addEventListener", JS_NewCFunction(ctx, js_doc_addEventListener, "addEventListener", 2));
|
|
346
|
+
JS_SetPropertyStr(ctx, doc, "dispatchEvent", JS_NewCFunction(ctx, js_doc_dispatchEvent, "dispatchEvent", 1));
|
|
347
|
+
JS_FreeValue(ctx, doc);
|
|
348
|
+
|
|
349
|
+
JS_FreeValue(ctx, global);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Event/CustomEvent constructors + prototype (preventDefault/stopPropagation/
|
|
4
|
+
// stopImmediatePropagation), and addEventListener/removeEventListener/
|
|
5
|
+
// dispatchEvent for both elements and the document object.
|
|
6
|
+
//
|
|
7
|
+
// install(ctx) must run after ElementBindings::install(ctx) (it fetches the
|
|
8
|
+
// Element class prototype via JS_GetClassProto to attach the event methods)
|
|
9
|
+
// and after DocumentBindings::install(ctx) (it fetches globalThis.document to
|
|
10
|
+
// attach addEventListener/dispatchEvent there too).
|
|
11
|
+
|
|
12
|
+
#include "quickjs.h"
|
|
13
|
+
|
|
14
|
+
namespace margelo::nitro::nitrojsdom {
|
|
15
|
+
|
|
16
|
+
class EventBindings {
|
|
17
|
+
public:
|
|
18
|
+
static void install(JSContext* ctx);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
} // namespace margelo::nitro::nitrojsdom
|