@salve-software/react-native-nitro-jsdom 1.1.0 → 2.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.
Files changed (35) hide show
  1. package/android/CMakeLists.txt +4 -0
  2. package/cpp/HybridHtmlSandbox.cpp +7 -3
  3. package/cpp/lexbor/LexborDocument.cpp +62 -0
  4. package/cpp/lexbor/LexborDocument.hpp +14 -0
  5. package/cpp/quickjs/DOMBindings.cpp +9 -1
  6. package/cpp/quickjs/DOMBindingsInternal.cpp +67 -0
  7. package/cpp/quickjs/DOMBindingsInternal.hpp +39 -0
  8. package/cpp/quickjs/QuickJSRuntime.cpp +13 -0
  9. package/cpp/quickjs/QuickJSRuntime.hpp +28 -0
  10. package/cpp/quickjs/bindings/AbortBindings.cpp +14 -0
  11. package/cpp/quickjs/bindings/BlobBindings.cpp +28 -1
  12. package/cpp/quickjs/bindings/BlobBindings.hpp +3 -3
  13. package/cpp/quickjs/bindings/CSSOMBindings.cpp +46 -0
  14. package/cpp/quickjs/bindings/CSSOMBindings.hpp +3 -1
  15. package/cpp/quickjs/bindings/DOMParserBindings.cpp +299 -0
  16. package/cpp/quickjs/bindings/DOMParserBindings.hpp +45 -0
  17. package/cpp/quickjs/bindings/DocumentBindings.cpp +166 -11
  18. package/cpp/quickjs/bindings/ElementBindings.cpp +296 -20
  19. package/cpp/quickjs/bindings/EventBindings.cpp +277 -5
  20. package/cpp/quickjs/bindings/EventBindings.hpp +3 -0
  21. package/cpp/quickjs/bindings/EventTargetBindings.cpp +75 -0
  22. package/cpp/quickjs/bindings/EventTargetBindings.hpp +24 -0
  23. package/cpp/quickjs/bindings/FetchBindings.cpp +43 -8
  24. package/cpp/quickjs/bindings/FormBindings.cpp +223 -0
  25. package/cpp/quickjs/bindings/FormBindings.hpp +9 -5
  26. package/cpp/quickjs/bindings/LayoutStubBindings.cpp +104 -0
  27. package/cpp/quickjs/bindings/LayoutStubBindings.hpp +28 -0
  28. package/cpp/quickjs/bindings/LiveCollectionBindings.cpp +45 -0
  29. package/cpp/quickjs/bindings/TimerBindings.cpp +79 -0
  30. package/cpp/quickjs/bindings/TreeWalkerBindings.cpp +306 -0
  31. package/cpp/quickjs/bindings/TreeWalkerBindings.hpp +28 -0
  32. package/cpp/quickjs/bindings/UrlBindings.cpp +9 -0
  33. package/cpp/quickjs/bindings/WindowBindings.cpp +286 -4
  34. package/cpp/quickjs/bindings/WindowBindings.hpp +3 -2
  35. package/package.json +17 -2
@@ -0,0 +1,306 @@
1
+ #include "TreeWalkerBindings.hpp"
2
+ #include <cstring>
3
+
4
+ namespace margelo::nitro::nitrojsdom {
5
+
6
+ namespace {
7
+
8
+ const char* kTreeWalkerBootstrapScript = R"JS(
9
+ (function() {
10
+ var FILTER_ACCEPT = 1, FILTER_REJECT = 2, FILTER_SKIP = 3;
11
+
12
+ var NodeFilter = {
13
+ FILTER_ACCEPT: FILTER_ACCEPT,
14
+ FILTER_REJECT: FILTER_REJECT,
15
+ FILTER_SKIP: FILTER_SKIP,
16
+ SHOW_ALL: 0xFFFFFFFF,
17
+ SHOW_ELEMENT: 0x1,
18
+ SHOW_ATTRIBUTE: 0x2,
19
+ SHOW_TEXT: 0x4,
20
+ SHOW_CDATA_SECTION: 0x8,
21
+ SHOW_ENTITY_REFERENCE: 0x10,
22
+ SHOW_ENTITY: 0x20,
23
+ SHOW_PROCESSING_INSTRUCTION: 0x40,
24
+ SHOW_COMMENT: 0x80,
25
+ SHOW_DOCUMENT: 0x100,
26
+ SHOW_DOCUMENT_TYPE: 0x200,
27
+ SHOW_DOCUMENT_FRAGMENT: 0x400,
28
+ SHOW_NOTATION: 0x800,
29
+ };
30
+ globalThis.NodeFilter = NodeFilter;
31
+
32
+ function normalizeFilter(filter) {
33
+ if (filter === undefined || filter === null) return null;
34
+ if (typeof filter === 'function') return filter;
35
+ if (typeof filter.acceptNode === 'function') {
36
+ return function(node) { return filter.acceptNode(node); };
37
+ }
38
+ return null;
39
+ }
40
+
41
+ // Shared by TreeWalker and NodeIterator — both store whatToShow/_filterFn/
42
+ // _active the same way, so this reads `this` structurally rather than
43
+ // needing a common base class.
44
+ function applyFilter(walker, node) {
45
+ if (walker._active) {
46
+ throw new DOMException('Recursive node filtering', 'InvalidStateError');
47
+ }
48
+ var n = node.nodeType - 1;
49
+ if (!((1 << n) & walker.whatToShow)) return FILTER_SKIP;
50
+ if (walker._filterFn === null) return FILTER_ACCEPT;
51
+ walker._active = true;
52
+ var result;
53
+ try {
54
+ result = walker._filterFn(node);
55
+ } finally {
56
+ walker._active = false;
57
+ }
58
+ return result;
59
+ }
60
+
61
+ // ── TreeWalker ───────────────────────────────────────────────────────
62
+ // Ported from jsdom's TreeWalker-impl.js — see header comment.
63
+
64
+ function TreeWalker(root, whatToShow, filter) {
65
+ this.root = root;
66
+ this.whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (whatToShow >>> 0);
67
+ this.filter = filter === undefined ? null : filter;
68
+ this._filterFn = normalizeFilter(filter);
69
+ this._active = false;
70
+ this._currentNode = root;
71
+ }
72
+ Object.defineProperty(TreeWalker.prototype, 'currentNode', {
73
+ get: function() { return this._currentNode; },
74
+ set: function(node) {
75
+ if (node === null || node === undefined) {
76
+ throw new DOMException('Cannot set currentNode to null', 'NotSupportedError');
77
+ }
78
+ this._currentNode = node;
79
+ },
80
+ enumerable: true,
81
+ });
82
+
83
+ TreeWalker.prototype.parentNode = function() {
84
+ var node = this._currentNode;
85
+ while (node !== null && node !== this.root) {
86
+ node = node.parentNode;
87
+ if (node !== null && applyFilter(this, node) === FILTER_ACCEPT) {
88
+ this._currentNode = node;
89
+ return node;
90
+ }
91
+ }
92
+ return null;
93
+ };
94
+
95
+ function traverseChildren(walker, forward) {
96
+ var node = walker._currentNode;
97
+ node = forward ? node.firstChild : node.lastChild;
98
+ if (node === null) return null;
99
+
100
+ mainLoop:
101
+ for (;;) {
102
+ var result = applyFilter(walker, node);
103
+ if (result === FILTER_ACCEPT) {
104
+ walker._currentNode = node;
105
+ return node;
106
+ }
107
+ if (result === FILTER_SKIP) {
108
+ var child = forward ? node.firstChild : node.lastChild;
109
+ if (child !== null) { node = child; continue mainLoop; }
110
+ }
111
+ for (;;) {
112
+ var sibling = forward ? node.nextSibling : node.previousSibling;
113
+ if (sibling !== null) { node = sibling; continue mainLoop; }
114
+ var parent = node.parentNode;
115
+ if (parent === null || parent === walker.root || parent === walker._currentNode) return null;
116
+ node = parent;
117
+ }
118
+ }
119
+ }
120
+ TreeWalker.prototype.firstChild = function() { return traverseChildren(this, true); };
121
+ TreeWalker.prototype.lastChild = function() { return traverseChildren(this, false); };
122
+
123
+ function traverseSiblings(walker, forward) {
124
+ var node = walker._currentNode;
125
+ if (node === walker.root) return null;
126
+
127
+ for (;;) {
128
+ var sibling = forward ? node.nextSibling : node.previousSibling;
129
+ while (sibling !== null) {
130
+ node = sibling;
131
+ var result = applyFilter(walker, node);
132
+ if (result === FILTER_ACCEPT) {
133
+ walker._currentNode = node;
134
+ return node;
135
+ }
136
+ sibling = forward ? node.firstChild : node.lastChild;
137
+ if (result === FILTER_REJECT || sibling === null) {
138
+ sibling = forward ? node.nextSibling : node.previousSibling;
139
+ }
140
+ }
141
+ node = node.parentNode;
142
+ if (node === null || node === walker.root) return null;
143
+ if (applyFilter(walker, node) === FILTER_ACCEPT) return null;
144
+ }
145
+ }
146
+ TreeWalker.prototype.previousSibling = function() { return traverseSiblings(this, false); };
147
+ TreeWalker.prototype.nextSibling = function() { return traverseSiblings(this, true); };
148
+
149
+ TreeWalker.prototype.previousNode = function() {
150
+ var node = this._currentNode;
151
+ while (node !== this.root) {
152
+ var sibling = node.previousSibling;
153
+ while (sibling !== null) {
154
+ node = sibling;
155
+ var result = applyFilter(this, node);
156
+ while (result !== FILTER_REJECT && node.firstChild !== null) {
157
+ node = node.lastChild;
158
+ result = applyFilter(this, node);
159
+ }
160
+ if (result === FILTER_ACCEPT) {
161
+ this._currentNode = node;
162
+ return node;
163
+ }
164
+ sibling = node.previousSibling;
165
+ }
166
+ if (node === this.root || node.parentNode === null) return null;
167
+ node = node.parentNode;
168
+ if (applyFilter(this, node) === FILTER_ACCEPT) {
169
+ this._currentNode = node;
170
+ return node;
171
+ }
172
+ }
173
+ return null;
174
+ };
175
+
176
+ TreeWalker.prototype.nextNode = function() {
177
+ var node = this._currentNode;
178
+ var result = FILTER_ACCEPT;
179
+ for (;;) {
180
+ while (result !== FILTER_REJECT && node.firstChild !== null) {
181
+ node = node.firstChild;
182
+ result = applyFilter(this, node);
183
+ if (result === FILTER_ACCEPT) {
184
+ this._currentNode = node;
185
+ return node;
186
+ }
187
+ }
188
+ var sibling = null;
189
+ do {
190
+ if (node === this.root) return null;
191
+ sibling = node.nextSibling;
192
+ if (sibling !== null) { node = sibling; break; }
193
+ node = node.parentNode;
194
+ } while (node !== null);
195
+ if (node === null) return null;
196
+ result = applyFilter(this, node);
197
+ if (result === FILTER_ACCEPT) {
198
+ this._currentNode = node;
199
+ return node;
200
+ }
201
+ }
202
+ };
203
+
204
+ globalThis.TreeWalker = TreeWalker;
205
+
206
+ // ── NodeIterator ─────────────────────────────────────────────────────
207
+ // Ported from jsdom's NodeIterator-impl.js, with domSymbolTree.following()/
208
+ // preceding() replaced by direct tree-order walks over the same node
209
+ // properties TreeWalker uses.
210
+
211
+ function followingNode(node, root) {
212
+ if (node.firstChild !== null) return node.firstChild;
213
+ var cur = node;
214
+ while (cur !== null && cur !== root) {
215
+ if (cur.nextSibling !== null) return cur.nextSibling;
216
+ cur = cur.parentNode;
217
+ }
218
+ return null;
219
+ }
220
+ function lastInclusiveDescendant(node) {
221
+ while (node.lastChild !== null) node = node.lastChild;
222
+ return node;
223
+ }
224
+ function precedingNode(node, root) {
225
+ if (node === root) return null;
226
+ var sibling = node.previousSibling;
227
+ if (sibling !== null) return lastInclusiveDescendant(sibling);
228
+ return node.parentNode;
229
+ }
230
+
231
+ function NodeIterator(root, whatToShow, filter) {
232
+ this.root = root;
233
+ this.whatToShow = whatToShow === undefined ? NodeFilter.SHOW_ALL : (whatToShow >>> 0);
234
+ this.filter = filter === undefined ? null : filter;
235
+ this._filterFn = normalizeFilter(filter);
236
+ this._active = false;
237
+ this._referenceNode = root;
238
+ this._pointerBeforeReferenceNode = true;
239
+ }
240
+ Object.defineProperty(NodeIterator.prototype, 'referenceNode', {
241
+ get: function() { return this._referenceNode; },
242
+ enumerable: true,
243
+ });
244
+ Object.defineProperty(NodeIterator.prototype, 'pointerBeforeReferenceNode', {
245
+ get: function() { return this._pointerBeforeReferenceNode; },
246
+ enumerable: true,
247
+ });
248
+
249
+ function iteratorTraverse(it, forward) {
250
+ var node = it._referenceNode;
251
+ var beforeNode = it._pointerBeforeReferenceNode;
252
+ for (;;) {
253
+ if (forward) {
254
+ if (!beforeNode) {
255
+ node = followingNode(node, it.root);
256
+ if (node === null) return null;
257
+ }
258
+ beforeNode = false;
259
+ } else {
260
+ if (beforeNode) {
261
+ node = precedingNode(node, it.root);
262
+ if (node === null) return null;
263
+ }
264
+ beforeNode = true;
265
+ }
266
+ if (applyFilter(it, node) === FILTER_ACCEPT) break;
267
+ }
268
+ it._referenceNode = node;
269
+ it._pointerBeforeReferenceNode = beforeNode;
270
+ return node;
271
+ }
272
+
273
+ NodeIterator.prototype.nextNode = function() { return iteratorTraverse(this, true); };
274
+ NodeIterator.prototype.previousNode = function() { return iteratorTraverse(this, false); };
275
+ NodeIterator.prototype.detach = function() {};
276
+
277
+ globalThis.NodeIterator = NodeIterator;
278
+
279
+ // ── document.createTreeWalker / createNodeIterator ──────────────────
280
+ document.createTreeWalker = function(root, whatToShow, filter) {
281
+ if (!(root instanceof Node)) {
282
+ throw new TypeError("Failed to execute 'createTreeWalker' on 'Document': parameter 1 is not of type 'Node'.");
283
+ }
284
+ return new TreeWalker(root, whatToShow, filter);
285
+ };
286
+ document.createNodeIterator = function(root, whatToShow, filter) {
287
+ if (!(root instanceof Node)) {
288
+ throw new TypeError("Failed to execute 'createNodeIterator' on 'Document': parameter 1 is not of type 'Node'.");
289
+ }
290
+ return new NodeIterator(root, whatToShow, filter);
291
+ };
292
+ })();
293
+ )JS";
294
+
295
+ } // namespace
296
+
297
+ void TreeWalkerBindings::install(JSContext* ctx) {
298
+ JSValue result = JS_Eval(ctx, kTreeWalkerBootstrapScript, strlen(kTreeWalkerBootstrapScript),
299
+ "<tree-walker-bootstrap>", JS_EVAL_TYPE_GLOBAL);
300
+ if (JS_IsException(result)) {
301
+ JS_FreeValue(ctx, JS_GetException(ctx));
302
+ }
303
+ JS_FreeValue(ctx, result);
304
+ }
305
+
306
+ } // namespace margelo::nitro::nitrojsdom
@@ -0,0 +1,28 @@
1
+ #pragma once
2
+
3
+ // NodeFilter, TreeWalker, NodeIterator, and document.createTreeWalker() /
4
+ // createNodeIterator(). Pure JS on top of the Node traversal properties
5
+ // ElementBindings already exposes (firstChild/lastChild/nextSibling/
6
+ // previousSibling/parentNode/nodeType) — no Lexbor access needed.
7
+ //
8
+ // The traversal algorithms (TreeWalker.previousSibling()/nextSibling() in
9
+ // particular — they can descend into an accepted-skip subtree looking for a
10
+ // matching descendant, then ascend) are subtle enough that this is a direct
11
+ // port of jsdom's TreeWalker-impl.js/NodeIterator-impl.js/helpers.js, not a
12
+ // from-scratch implementation, to avoid a plausible-looking-but-wrong
13
+ // backtracking bug. NodeIterator's `_referenceNode`/`_pointerBeforeReferenceNode`
14
+ // walk uses simple tree-order successor/predecessor helpers here in place of
15
+ // jsdom's domSymbolTree.following()/preceding() dependency.
16
+ //
17
+ // Must run after ElementBindings (globalThis.Element/Node) and
18
+ // DocumentBindings (globalThis.document).
19
+
20
+ #include "quickjs.h"
21
+
22
+ namespace margelo::nitro::nitrojsdom {
23
+
24
+ struct TreeWalkerBindings {
25
+ static void install(JSContext* ctx);
26
+ };
27
+
28
+ } // namespace margelo::nitro::nitrojsdom
@@ -268,6 +268,15 @@ const char* kUrlBootstrapScript = R"JS(
268
268
  URL.prototype.toString = function() { return this._href; };
269
269
  URL.prototype.toJSON = function() { return this._href; };
270
270
 
271
+ URL.canParse = function(url, base) {
272
+ try {
273
+ new URL(url, base);
274
+ return true;
275
+ } catch (e) {
276
+ return false;
277
+ }
278
+ };
279
+
271
280
  globalThis.URL = URL;
272
281
  })();
273
282
  )JS";