@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.
- package/android/CMakeLists.txt +4 -0
- package/cpp/HybridHtmlSandbox.cpp +7 -3
- package/cpp/lexbor/LexborDocument.cpp +62 -0
- package/cpp/lexbor/LexborDocument.hpp +14 -0
- package/cpp/quickjs/DOMBindings.cpp +9 -1
- package/cpp/quickjs/DOMBindingsInternal.cpp +67 -0
- package/cpp/quickjs/DOMBindingsInternal.hpp +39 -0
- package/cpp/quickjs/QuickJSRuntime.cpp +13 -0
- package/cpp/quickjs/QuickJSRuntime.hpp +28 -0
- package/cpp/quickjs/bindings/AbortBindings.cpp +14 -0
- package/cpp/quickjs/bindings/BlobBindings.cpp +28 -1
- package/cpp/quickjs/bindings/BlobBindings.hpp +3 -3
- package/cpp/quickjs/bindings/CSSOMBindings.cpp +46 -0
- package/cpp/quickjs/bindings/CSSOMBindings.hpp +3 -1
- package/cpp/quickjs/bindings/DOMParserBindings.cpp +299 -0
- package/cpp/quickjs/bindings/DOMParserBindings.hpp +45 -0
- package/cpp/quickjs/bindings/DocumentBindings.cpp +166 -11
- package/cpp/quickjs/bindings/ElementBindings.cpp +296 -20
- package/cpp/quickjs/bindings/EventBindings.cpp +277 -5
- package/cpp/quickjs/bindings/EventBindings.hpp +3 -0
- package/cpp/quickjs/bindings/EventTargetBindings.cpp +75 -0
- package/cpp/quickjs/bindings/EventTargetBindings.hpp +24 -0
- package/cpp/quickjs/bindings/FetchBindings.cpp +43 -8
- package/cpp/quickjs/bindings/FormBindings.cpp +223 -0
- package/cpp/quickjs/bindings/FormBindings.hpp +9 -5
- package/cpp/quickjs/bindings/LayoutStubBindings.cpp +104 -0
- package/cpp/quickjs/bindings/LayoutStubBindings.hpp +28 -0
- package/cpp/quickjs/bindings/LiveCollectionBindings.cpp +45 -0
- package/cpp/quickjs/bindings/TimerBindings.cpp +79 -0
- package/cpp/quickjs/bindings/TreeWalkerBindings.cpp +306 -0
- package/cpp/quickjs/bindings/TreeWalkerBindings.hpp +28 -0
- package/cpp/quickjs/bindings/UrlBindings.cpp +9 -0
- package/cpp/quickjs/bindings/WindowBindings.cpp +286 -4
- package/cpp/quickjs/bindings/WindowBindings.hpp +3 -2
- package/package.json +17 -2
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
#include <lexbor/html/html.h>
|
|
12
12
|
#include <lexbor/dom/dom.h>
|
|
13
13
|
#include <lexbor/dom/interfaces/character_data.h>
|
|
14
|
+
#include <lexbor/dom/interfaces/attr.h>
|
|
15
|
+
#include <lexbor/ns/const.h>
|
|
16
|
+
#include <lexbor/ns/ns.h>
|
|
14
17
|
#include <cctype>
|
|
15
18
|
#include <cstring>
|
|
16
19
|
#include <optional>
|
|
@@ -29,10 +32,18 @@ JSValue js_el_get_tagName(JSContext* ctx, JSValue this_val) {
|
|
|
29
32
|
auto* el = unwrap_element(ctx, this_val);
|
|
30
33
|
if (!el) return JS_NewString(ctx, "");
|
|
31
34
|
size_t len = 0;
|
|
32
|
-
|
|
35
|
+
// qualified_name (prefix:localName) when the element has a namespace
|
|
36
|
+
// prefix (createElementNS('...', 'prefix:name')), falling back to the
|
|
37
|
+
// plain local name otherwise.
|
|
38
|
+
const lxb_char_t* name = lxb_dom_element_qualified_name(el, &len);
|
|
33
39
|
if (!name || len == 0) return JS_NewString(ctx, "");
|
|
34
40
|
std::string r(reinterpret_cast<const char*>(name), len);
|
|
35
|
-
|
|
41
|
+
// Only HTML-namespace elements are uppercased (per spec); SVG/MathML/custom
|
|
42
|
+
// namespace elements keep their (lexbor-lowercased) local name as-is.
|
|
43
|
+
auto* node = reinterpret_cast<lxb_dom_node_t*>(el);
|
|
44
|
+
if (node->ns == LXB_NS_HTML) {
|
|
45
|
+
for (auto& c : r) c = (char)std::toupper((unsigned char)c);
|
|
46
|
+
}
|
|
36
47
|
return JS_NewStringLen(ctx, r.c_str(), r.size());
|
|
37
48
|
}
|
|
38
49
|
|
|
@@ -223,7 +234,7 @@ JSValue js_el_set_value(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
223
234
|
if (has_obs) rctx->mutation_observers->disconnectDetachedTargets(old_children);
|
|
224
235
|
}
|
|
225
236
|
|
|
226
|
-
|
|
237
|
+
doc_for_node(ctx, el)->setTextContentOnEl(el, str);
|
|
227
238
|
|
|
228
239
|
if (has_obs) {
|
|
229
240
|
std::vector<void*> new_children;
|
|
@@ -340,7 +351,7 @@ JSValue js_el_set_textContent(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
340
351
|
if (has_observers) rctx->mutation_observers->disconnectDetachedTargets(old_children);
|
|
341
352
|
}
|
|
342
353
|
}
|
|
343
|
-
|
|
354
|
+
doc_for_node(ctx, node)->setTextContentOnEl(el, str);
|
|
344
355
|
if (has_observers) {
|
|
345
356
|
std::vector<void*> new_children;
|
|
346
357
|
lxb_dom_node_t* child = node->first_child;
|
|
@@ -385,7 +396,7 @@ JSValue js_el_set_innerHTML(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
385
396
|
if (has_observers) rctx->mutation_observers->disconnectDetachedTargets(old_children);
|
|
386
397
|
}
|
|
387
398
|
|
|
388
|
-
|
|
399
|
+
doc_for_node(ctx, el)->setInnerHTMLOnEl(el, html);
|
|
389
400
|
JS_FreeCString(ctx, html);
|
|
390
401
|
|
|
391
402
|
if (has_observers) {
|
|
@@ -530,6 +541,16 @@ JSValue js_el_set_nodeValue(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
530
541
|
return JS_UNDEFINED;
|
|
531
542
|
}
|
|
532
543
|
|
|
544
|
+
JSValue js_el_get_data_length(JSContext* ctx, JSValue this_val) {
|
|
545
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
546
|
+
if (!node) return JS_UNDEFINED;
|
|
547
|
+
if (node->type != LXB_DOM_NODE_TYPE_TEXT && node->type != LXB_DOM_NODE_TYPE_COMMENT) return JS_UNDEFINED;
|
|
548
|
+
JSValue str_val = js_el_get_nodeValue(ctx, this_val);
|
|
549
|
+
JSValue len_val = JS_GetPropertyStr(ctx, str_val, "length");
|
|
550
|
+
JS_FreeValue(ctx, str_val);
|
|
551
|
+
return len_val;
|
|
552
|
+
}
|
|
553
|
+
|
|
533
554
|
JSValue js_el_get_childNodes(JSContext* ctx, JSValue this_val) {
|
|
534
555
|
auto* node = unwrap_node(ctx, this_val);
|
|
535
556
|
if (!node) return JS_NewArray(ctx);
|
|
@@ -651,6 +672,93 @@ JSValue js_el_hasAttribute(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
651
672
|
return JS_NewBool(ctx, has);
|
|
652
673
|
}
|
|
653
674
|
|
|
675
|
+
// ── Namespaced attribute methods (getAttributeNS/setAttributeNS/...) ─────────
|
|
676
|
+
// Simplification: this sandbox doesn't track a real (namespace, localName)
|
|
677
|
+
// identity per attribute the way createElementNS()/namespaceURI() do for
|
|
678
|
+
// elements — building that would mean hand-constructing lxb_dom_attr_t nodes
|
|
679
|
+
// with their own ns/prefix fields, a much larger and riskier lift than the
|
|
680
|
+
// element side. Instead, attributes are matched by local name only (the
|
|
681
|
+
// qualified name with any "prefix:" stripped), which is spec-accurate for
|
|
682
|
+
// the common real-world case (a single xlink:href-style attribute) but not
|
|
683
|
+
// for two attributes sharing a local name across different namespaces — a
|
|
684
|
+
// case that essentially never comes up in embedded-widget scripts.
|
|
685
|
+
lxb_dom_attr_t* find_attr_by_local_name(lxb_dom_element_t* el, const std::string& localName) {
|
|
686
|
+
for (lxb_dom_attr_t* attr = lxb_dom_element_first_attribute(el); attr; attr = lxb_dom_element_next_attribute(attr)) {
|
|
687
|
+
size_t len = 0;
|
|
688
|
+
const lxb_char_t* qname = lxb_dom_attr_qualified_name(attr, &len);
|
|
689
|
+
if (!qname) continue;
|
|
690
|
+
std::string q(reinterpret_cast<const char*>(qname), len);
|
|
691
|
+
auto colon = q.find(':');
|
|
692
|
+
std::string local = (colon == std::string::npos) ? q : q.substr(colon + 1);
|
|
693
|
+
if (local == localName) return attr;
|
|
694
|
+
}
|
|
695
|
+
return nullptr;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
JSValue js_el_getAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
699
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
700
|
+
if (!el || argc < 2) return JS_NULL;
|
|
701
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
702
|
+
if (!local) return JS_NULL;
|
|
703
|
+
auto* attr = find_attr_by_local_name(el, local);
|
|
704
|
+
JS_FreeCString(ctx, local);
|
|
705
|
+
if (!attr) return JS_NULL;
|
|
706
|
+
size_t len = 0;
|
|
707
|
+
const lxb_char_t* val = lxb_dom_attr_value(attr, &len);
|
|
708
|
+
return val ? JS_NewStringLen(ctx, reinterpret_cast<const char*>(val), len) : JS_NewString(ctx, "");
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
JSValue js_el_hasAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
712
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
713
|
+
if (!el || argc < 2) return JS_FALSE;
|
|
714
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
715
|
+
if (!local) return JS_FALSE;
|
|
716
|
+
bool has = find_attr_by_local_name(el, local) != nullptr;
|
|
717
|
+
JS_FreeCString(ctx, local);
|
|
718
|
+
return JS_NewBool(ctx, has);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
JSValue js_el_setAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
722
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
723
|
+
if (!el || argc < 3) return JS_UNDEFINED;
|
|
724
|
+
const char* qualified_name = JS_ToCString(ctx, argv[1]);
|
|
725
|
+
const char* value = JS_ToCString(ctx, argv[2]);
|
|
726
|
+
if (qualified_name && value) {
|
|
727
|
+
std::string qn(qualified_name);
|
|
728
|
+
auto colon = qn.find(':');
|
|
729
|
+
std::string local = (colon == std::string::npos) ? qn : qn.substr(colon + 1);
|
|
730
|
+
|
|
731
|
+
auto* existing = find_attr_by_local_name(el, local);
|
|
732
|
+
if (existing) {
|
|
733
|
+
lxb_dom_attr_set_existing_value(existing,
|
|
734
|
+
reinterpret_cast<const lxb_char_t*>(value), strlen(value));
|
|
735
|
+
} else {
|
|
736
|
+
lxb_dom_element_set_attribute(el,
|
|
737
|
+
reinterpret_cast<const lxb_char_t*>(qualified_name), qn.size(),
|
|
738
|
+
reinterpret_cast<const lxb_char_t*>(value), strlen(value));
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (qualified_name) JS_FreeCString(ctx, qualified_name);
|
|
742
|
+
if (value) JS_FreeCString(ctx, value);
|
|
743
|
+
return JS_UNDEFINED;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
JSValue js_el_removeAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
747
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
748
|
+
if (!el || argc < 2) return JS_UNDEFINED;
|
|
749
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
750
|
+
if (local) {
|
|
751
|
+
auto* attr = find_attr_by_local_name(el, local);
|
|
752
|
+
if (attr) {
|
|
753
|
+
size_t qlen = 0;
|
|
754
|
+
const lxb_char_t* qname = lxb_dom_attr_qualified_name(attr, &qlen);
|
|
755
|
+
if (qname) lxb_dom_element_remove_attribute(el, qname, qlen);
|
|
756
|
+
}
|
|
757
|
+
JS_FreeCString(ctx, local);
|
|
758
|
+
}
|
|
759
|
+
return JS_UNDEFINED;
|
|
760
|
+
}
|
|
761
|
+
|
|
654
762
|
// Inserts `node` via `insert_one`, expanding DocumentFragments into their
|
|
655
763
|
// children first (per the DOM's "insert a node" algorithm — a fragment is
|
|
656
764
|
// never itself part of the resulting tree, only its children are moved in).
|
|
@@ -821,12 +929,15 @@ JSValue js_el_replaceChild(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
821
929
|
return JS_DupValue(ctx, argv[1]); // returns the replaced (old) child, per spec
|
|
822
930
|
}
|
|
823
931
|
|
|
824
|
-
|
|
932
|
+
// `context_node` supplies the owning document for a string argument coerced
|
|
933
|
+
// into a new text node (see doc_for_node()) — irrelevant when `val` is
|
|
934
|
+
// already a node.
|
|
935
|
+
lxb_dom_node_t* js_to_node_or_text(JSContext* ctx, JSValue val, lxb_dom_node_t* context_node) {
|
|
825
936
|
auto* node = unwrap_node(ctx, val);
|
|
826
937
|
if (node) return node;
|
|
827
938
|
const char* str = JS_ToCString(ctx, val);
|
|
828
939
|
if (!str) return nullptr;
|
|
829
|
-
void* text =
|
|
940
|
+
void* text = doc_for_node(ctx, context_node)->createTextNode(str);
|
|
830
941
|
JS_FreeCString(ctx, str);
|
|
831
942
|
return static_cast<lxb_dom_node_t*>(text);
|
|
832
943
|
}
|
|
@@ -838,7 +949,7 @@ JSValue js_el_before(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
838
949
|
|
|
839
950
|
std::vector<void*> inserted;
|
|
840
951
|
for (int i = 0; i < argc; i++) {
|
|
841
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
952
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
842
953
|
if (!n) continue;
|
|
843
954
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_before(node, one); });
|
|
844
955
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -860,7 +971,7 @@ JSValue js_el_after(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
|
860
971
|
|
|
861
972
|
std::vector<void*> inserted;
|
|
862
973
|
for (int i = 0; i < argc; i++) {
|
|
863
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
974
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
864
975
|
if (!n) continue;
|
|
865
976
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) {
|
|
866
977
|
if (ref) lxb_dom_node_insert_before(ref, one); else lxb_dom_node_insert_child(parent, one);
|
|
@@ -884,7 +995,7 @@ JSValue js_el_replaceWith(JSContext* ctx, JSValue this_val, int argc, JSValue* a
|
|
|
884
995
|
|
|
885
996
|
std::vector<void*> inserted;
|
|
886
997
|
for (int i = 0; i < argc; i++) {
|
|
887
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
998
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
888
999
|
if (!n) continue;
|
|
889
1000
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_before(node, one); });
|
|
890
1001
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -906,7 +1017,7 @@ JSValue js_el_append(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
906
1017
|
|
|
907
1018
|
std::vector<void*> inserted;
|
|
908
1019
|
for (int i = 0; i < argc; i++) {
|
|
909
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1020
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], parent_node);
|
|
910
1021
|
if (!n) continue;
|
|
911
1022
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_child(parent_node, one); });
|
|
912
1023
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -927,7 +1038,7 @@ JSValue js_el_prepend(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
927
1038
|
|
|
928
1039
|
std::vector<void*> inserted;
|
|
929
1040
|
for (int i = 0; i < argc; i++) {
|
|
930
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1041
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], parent_node);
|
|
931
1042
|
if (!n) continue;
|
|
932
1043
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) {
|
|
933
1044
|
if (ref) lxb_dom_node_insert_before(ref, one); else lxb_dom_node_insert_child(parent_node, one);
|
|
@@ -948,11 +1059,12 @@ JSValue js_el_closest(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
948
1059
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
949
1060
|
if (!sel) return JS_NULL;
|
|
950
1061
|
|
|
1062
|
+
auto* doc = doc_for_node(ctx, lxb_dom_interface_node(el));
|
|
951
1063
|
JSValue result = JS_NULL;
|
|
952
1064
|
for (lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
953
1065
|
node && node->type == LXB_DOM_NODE_TYPE_ELEMENT;
|
|
954
1066
|
node = node->parent) {
|
|
955
|
-
if (
|
|
1067
|
+
if (doc->matchesSelector(lxb_dom_interface_element(node), sel)) {
|
|
956
1068
|
result = make_element(ctx, node);
|
|
957
1069
|
break;
|
|
958
1070
|
}
|
|
@@ -975,7 +1087,7 @@ JSValue js_el_insertAdjacentHTML(JSContext* ctx, JSValue this_val, int argc, JSV
|
|
|
975
1087
|
JS_FreeCString(ctx, position);
|
|
976
1088
|
|
|
977
1089
|
lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
978
|
-
auto parsed =
|
|
1090
|
+
auto parsed = doc_for_node(ctx, node)->parseFragmentNodes(el, html);
|
|
979
1091
|
JS_FreeCString(ctx, html);
|
|
980
1092
|
if (parsed.empty()) return JS_UNDEFINED;
|
|
981
1093
|
|
|
@@ -1028,7 +1140,7 @@ JSValue js_el_matches(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
1028
1140
|
if (!el || argc < 1) return JS_FALSE;
|
|
1029
1141
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1030
1142
|
if (!sel) return JS_FALSE;
|
|
1031
|
-
bool result =
|
|
1143
|
+
bool result = doc_for_node(ctx, el)->matchesSelector(el, sel);
|
|
1032
1144
|
JS_FreeCString(ctx, sel);
|
|
1033
1145
|
return JS_NewBool(ctx, result);
|
|
1034
1146
|
}
|
|
@@ -1038,7 +1150,7 @@ JSValue js_el_querySelector(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
1038
1150
|
if (!el || argc < 1) return JS_NULL;
|
|
1039
1151
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1040
1152
|
if (!sel) return JS_NULL;
|
|
1041
|
-
void* found =
|
|
1153
|
+
void* found = doc_for_node(ctx, el)->querySelectorFromEl(el, sel);
|
|
1042
1154
|
JS_FreeCString(ctx, sel);
|
|
1043
1155
|
return make_element(ctx, found);
|
|
1044
1156
|
}
|
|
@@ -1048,7 +1160,7 @@ JSValue js_el_querySelectorAll(JSContext* ctx, JSValue this_val, int argc, JSVal
|
|
|
1048
1160
|
if (!el || argc < 1) return JS_NewArray(ctx);
|
|
1049
1161
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1050
1162
|
if (!sel) return JS_NewArray(ctx);
|
|
1051
|
-
auto results =
|
|
1163
|
+
auto results = doc_for_node(ctx, el)->querySelectorAllFromEl(el, sel);
|
|
1052
1164
|
JS_FreeCString(ctx, sel);
|
|
1053
1165
|
return make_element_array(ctx, results);
|
|
1054
1166
|
}
|
|
@@ -1154,6 +1266,14 @@ JSValue js_el_getBoundingClientRect(JSContext* ctx, JSValue this_val, int, JSVal
|
|
|
1154
1266
|
return rect;
|
|
1155
1267
|
}
|
|
1156
1268
|
|
|
1269
|
+
JSValue js_el_getClientRects(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1270
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1271
|
+
if (!el) return JS_NewArray(ctx);
|
|
1272
|
+
JSValue arr = JS_NewArray(ctx);
|
|
1273
|
+
JS_SetPropertyUint32(ctx, arr, 0, js_el_getBoundingClientRect(ctx, this_val, argc, argv));
|
|
1274
|
+
return arr;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1157
1277
|
JSValue js_el_isSameNode(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1158
1278
|
auto* node = unwrap_node(ctx, this_val);
|
|
1159
1279
|
if (!node || argc < 1) return JS_FALSE;
|
|
@@ -1229,7 +1349,7 @@ JSValue js_el_normalize(JSContext* ctx, JSValue this_val, int, JSValue*) {
|
|
|
1229
1349
|
invalidate_node_cache_batch(ctx, get_ctx(ctx), text_nodes);
|
|
1230
1350
|
}
|
|
1231
1351
|
|
|
1232
|
-
|
|
1352
|
+
doc_for_node(ctx, node)->normalize(node);
|
|
1233
1353
|
return JS_UNDEFINED;
|
|
1234
1354
|
}
|
|
1235
1355
|
|
|
@@ -1238,7 +1358,75 @@ JSValue js_el_compareDocumentPosition(JSContext* ctx, JSValue this_val, int argc
|
|
|
1238
1358
|
if (!node || argc < 1) return JS_NewInt32(ctx, 0);
|
|
1239
1359
|
auto* other = unwrap_node(ctx, argv[0]);
|
|
1240
1360
|
if (!other) return JS_NewInt32(ctx, 0);
|
|
1241
|
-
return JS_NewInt32(ctx,
|
|
1361
|
+
return JS_NewInt32(ctx, doc_for_node(ctx, node)->compareDocumentPosition(node, other));
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
JSValue js_canonicalize_root_node(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
1365
|
+
if (argc < 1) return JS_UNDEFINED;
|
|
1366
|
+
auto* node = unwrap_node(ctx, argv[0]);
|
|
1367
|
+
if (node && node->type == LXB_DOM_NODE_TYPE_DOCUMENT && doc_for_node(ctx, node) == get_doc(ctx)) {
|
|
1368
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1369
|
+
JSValue doc_val = JS_GetPropertyStr(ctx, global, "document");
|
|
1370
|
+
JS_FreeValue(ctx, global);
|
|
1371
|
+
return doc_val;
|
|
1372
|
+
}
|
|
1373
|
+
return JS_DupValue(ctx, argv[0]);
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
JSValue js_el_get_ownerDocument(JSContext* ctx, JSValue this_val) {
|
|
1377
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
1378
|
+
if (!node) return JS_NULL;
|
|
1379
|
+
if (node->type == LXB_DOM_NODE_TYPE_DOCUMENT) return JS_NULL;
|
|
1380
|
+
|
|
1381
|
+
LexborDocument* owner = doc_for_node(ctx, node);
|
|
1382
|
+
if (owner == get_doc(ctx)) {
|
|
1383
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1384
|
+
JSValue doc_val = JS_GetPropertyStr(ctx, global, "document");
|
|
1385
|
+
JS_FreeValue(ctx, global);
|
|
1386
|
+
return doc_val;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
JSValue wrapper = get_document_wrapper(ctx, owner);
|
|
1390
|
+
if (!JS_IsUndefined(wrapper)) return wrapper;
|
|
1391
|
+
|
|
1392
|
+
void* html_doc = owner->documentHtmlPtr();
|
|
1393
|
+
void* doc_node = lxb_dom_interface_node(
|
|
1394
|
+
lxb_dom_interface_document(static_cast<lxb_html_document_t*>(html_doc)));
|
|
1395
|
+
return make_element(ctx, doc_node);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
JSValue js_el_get_namespaceURI(JSContext* ctx, JSValue this_val) {
|
|
1399
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
1400
|
+
if (!node) return JS_NULL;
|
|
1401
|
+
LexborDocument* owner = doc_for_node(ctx, node);
|
|
1402
|
+
std::string uri = owner ? owner->namespaceURI(node) : "";
|
|
1403
|
+
if (uri.empty()) return JS_NULL;
|
|
1404
|
+
return JS_NewStringLen(ctx, uri.data(), uri.size());
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Element.prefix — the namespace prefix set via createElementNS('ns', 'prefix:local'),
|
|
1408
|
+
// or null for an element with no prefix (the overwhelmingly common case).
|
|
1409
|
+
JSValue js_el_get_prefix(JSContext* ctx, JSValue this_val) {
|
|
1410
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1411
|
+
if (!el) return JS_NULL;
|
|
1412
|
+
auto* node = reinterpret_cast<lxb_dom_node_t*>(el);
|
|
1413
|
+
if (node->prefix == 0) return JS_NULL;
|
|
1414
|
+
const lxb_ns_prefix_data_t* data = lxb_ns_prefix_data_by_id(node->owner_document->prefix, node->prefix);
|
|
1415
|
+
if (!data) return JS_NULL;
|
|
1416
|
+
const lxb_char_t* str = lexbor_hash_entry_str(&data->entry);
|
|
1417
|
+
if (!str) return JS_NULL;
|
|
1418
|
+
return JS_NewStringLen(ctx, reinterpret_cast<const char*>(str), data->entry.length);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// Node.baseURI — always the document's URL in this sandbox (no <base> element
|
|
1422
|
+
// support/per-node override), so this just forwards to location.href.
|
|
1423
|
+
JSValue js_el_get_baseURI(JSContext* ctx, JSValue) {
|
|
1424
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1425
|
+
JSValue location = JS_GetPropertyStr(ctx, global, "location");
|
|
1426
|
+
JS_FreeValue(ctx, global);
|
|
1427
|
+
JSValue href = JS_GetPropertyStr(ctx, location, "href");
|
|
1428
|
+
JS_FreeValue(ctx, location);
|
|
1429
|
+
return href;
|
|
1242
1430
|
}
|
|
1243
1431
|
|
|
1244
1432
|
} // namespace
|
|
@@ -1273,7 +1461,12 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1273
1461
|
define_prop(ctx, node_proto, "nodeType", js_el_get_nodeType, nullptr);
|
|
1274
1462
|
define_prop(ctx, node_proto, "nodeName", js_el_get_nodeName, nullptr);
|
|
1275
1463
|
define_prop(ctx, node_proto, "nodeValue", js_el_get_nodeValue, js_el_set_nodeValue);
|
|
1464
|
+
define_prop(ctx, node_proto, "data", js_el_get_nodeValue, js_el_set_nodeValue);
|
|
1465
|
+
define_prop(ctx, node_proto, "length", js_el_get_data_length, nullptr);
|
|
1276
1466
|
define_prop(ctx, node_proto, "textContent", js_el_get_textContent, js_el_set_textContent);
|
|
1467
|
+
define_prop(ctx, node_proto, "ownerDocument", js_el_get_ownerDocument, nullptr);
|
|
1468
|
+
define_prop(ctx, node_proto, "namespaceURI", js_el_get_namespaceURI, nullptr);
|
|
1469
|
+
define_prop(ctx, node_proto, "baseURI", js_el_get_baseURI, nullptr);
|
|
1277
1470
|
define_prop(ctx, node_proto, "childNodes", js_el_get_childNodes, nullptr);
|
|
1278
1471
|
define_prop(ctx, node_proto, "firstChild", js_el_get_firstChild, nullptr);
|
|
1279
1472
|
define_prop(ctx, node_proto, "lastChild", js_el_get_lastChild, nullptr);
|
|
@@ -1282,6 +1475,8 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1282
1475
|
define_prop(ctx, node_proto, "parentNode", js_el_get_parentNode, nullptr);
|
|
1283
1476
|
define_prop(ctx, node_proto, "parentElement", js_el_get_parentElement, nullptr);
|
|
1284
1477
|
|
|
1478
|
+
define_node_type_constants(ctx, node_proto);
|
|
1479
|
+
|
|
1285
1480
|
JS_SetClassProto(ctx, js_node_class_id, JS_DupValue(ctx, node_proto));
|
|
1286
1481
|
|
|
1287
1482
|
// ── Element proto: inherits Node, adds Element-specific APIs ─────────────
|
|
@@ -1292,10 +1487,15 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1292
1487
|
JS_SetPropertyStr(ctx, proto, "setAttribute", JS_NewCFunction(ctx, js_el_setAttribute, "setAttribute", 2));
|
|
1293
1488
|
JS_SetPropertyStr(ctx, proto, "removeAttribute", JS_NewCFunction(ctx, js_el_removeAttribute, "removeAttribute", 1));
|
|
1294
1489
|
JS_SetPropertyStr(ctx, proto, "hasAttribute", JS_NewCFunction(ctx, js_el_hasAttribute, "hasAttribute", 1));
|
|
1490
|
+
JS_SetPropertyStr(ctx, proto, "getAttributeNS", JS_NewCFunction(ctx, js_el_getAttributeNS, "getAttributeNS", 2));
|
|
1491
|
+
JS_SetPropertyStr(ctx, proto, "setAttributeNS", JS_NewCFunction(ctx, js_el_setAttributeNS, "setAttributeNS", 3));
|
|
1492
|
+
JS_SetPropertyStr(ctx, proto, "removeAttributeNS", JS_NewCFunction(ctx, js_el_removeAttributeNS, "removeAttributeNS", 2));
|
|
1493
|
+
JS_SetPropertyStr(ctx, proto, "hasAttributeNS", JS_NewCFunction(ctx, js_el_hasAttributeNS, "hasAttributeNS", 2));
|
|
1295
1494
|
JS_SetPropertyStr(ctx, proto, "toggleAttribute", JS_NewCFunction(ctx, js_el_toggleAttribute, "toggleAttribute", 2));
|
|
1296
1495
|
JS_SetPropertyStr(ctx, proto, "getAttributeNames", JS_NewCFunction(ctx, js_el_getAttributeNames, "getAttributeNames", 0));
|
|
1297
1496
|
JS_SetPropertyStr(ctx, proto, "remove", JS_NewCFunction(ctx, js_el_remove, "remove", 0));
|
|
1298
1497
|
JS_SetPropertyStr(ctx, proto, "matches", JS_NewCFunction(ctx, js_el_matches, "matches", 1));
|
|
1498
|
+
JS_SetPropertyStr(ctx, proto, "webkitMatchesSelector", JS_NewCFunction(ctx, js_el_matches, "webkitMatchesSelector", 1));
|
|
1299
1499
|
JS_SetPropertyStr(ctx, proto, "querySelector", JS_NewCFunction(ctx, js_el_querySelector, "querySelector", 1));
|
|
1300
1500
|
JS_SetPropertyStr(ctx, proto, "querySelectorAll", JS_NewCFunction(ctx, js_el_querySelectorAll, "querySelectorAll", 1));
|
|
1301
1501
|
JS_SetPropertyStr(ctx, proto, "getElementsByClassName", JS_NewCFunction(ctx, js_el_getElementsByClassName, "getElementsByClassName", 1));
|
|
@@ -1305,14 +1505,17 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1305
1505
|
JS_SetPropertyStr(ctx, proto, "append", JS_NewCFunction(ctx, js_el_append, "append", 0));
|
|
1306
1506
|
JS_SetPropertyStr(ctx, proto, "prepend", JS_NewCFunction(ctx, js_el_prepend, "prepend", 0));
|
|
1307
1507
|
JS_SetPropertyStr(ctx, proto, "getBoundingClientRect", JS_NewCFunction(ctx, js_el_getBoundingClientRect, "getBoundingClientRect", 0));
|
|
1508
|
+
JS_SetPropertyStr(ctx, proto, "getClientRects", JS_NewCFunction(ctx, js_el_getClientRects, "getClientRects", 0));
|
|
1308
1509
|
|
|
1309
1510
|
define_prop(ctx, proto, "tagName", js_el_get_tagName, nullptr);
|
|
1511
|
+
define_prop(ctx, proto, "prefix", js_el_get_prefix, nullptr);
|
|
1310
1512
|
define_prop(ctx, proto, "id", js_el_get_id, js_el_set_id);
|
|
1311
1513
|
define_prop(ctx, proto, "className", js_el_get_className, js_el_set_className);
|
|
1312
1514
|
define_prop(ctx, proto, "value", js_el_get_value, js_el_set_value);
|
|
1313
1515
|
define_prop(ctx, proto, "checked", js_el_get_checked, js_el_set_checked);
|
|
1314
1516
|
define_prop(ctx, proto, "innerHTML", js_el_get_innerHTML, js_el_set_innerHTML);
|
|
1315
1517
|
define_prop(ctx, proto, "outerHTML", js_el_get_outerHTML, nullptr);
|
|
1518
|
+
define_prop(ctx, proto, "innerText", js_el_get_textContent, js_el_set_textContent);
|
|
1316
1519
|
define_prop(ctx, proto, "children", js_el_get_children, nullptr);
|
|
1317
1520
|
define_prop(ctx, proto, "firstElementChild", js_el_get_firstElementChild, nullptr);
|
|
1318
1521
|
define_prop(ctx, proto, "lastElementChild", js_el_get_lastElementChild, nullptr);
|
|
@@ -1329,14 +1532,87 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1329
1532
|
JSValue node_proto_ref = JS_GetClassProto(ctx, js_node_class_id);
|
|
1330
1533
|
JSValue element_proto_ref = JS_GetClassProto(ctx, js_element_class_id);
|
|
1331
1534
|
|
|
1332
|
-
|
|
1535
|
+
JSValue node_ctor = define_global_constructor(ctx, "Node", node_proto_ref);
|
|
1536
|
+
define_node_type_constants(ctx, node_ctor);
|
|
1537
|
+
JS_FreeValue(ctx, node_ctor);
|
|
1333
1538
|
JSValue element_ctor = define_global_constructor(ctx, "Element", element_proto_ref);
|
|
1334
1539
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
1335
1540
|
JS_SetPropertyStr(ctx, global, "HTMLElement", element_ctor);
|
|
1541
|
+
JS_SetPropertyStr(ctx, global, "__nativeCanonicalizeRootNode",
|
|
1542
|
+
JS_NewCFunction(ctx, js_canonicalize_root_node, "__nativeCanonicalizeRootNode", 1));
|
|
1336
1543
|
JS_FreeValue(ctx, global);
|
|
1337
1544
|
|
|
1338
1545
|
JS_FreeValue(ctx, node_proto_ref);
|
|
1339
1546
|
JS_FreeValue(ctx, element_proto_ref);
|
|
1547
|
+
|
|
1548
|
+
static const char* kGetRootNodeScript = R"JS(
|
|
1549
|
+
(function() {
|
|
1550
|
+
Node.prototype.getRootNode = function(options) {
|
|
1551
|
+
var composed = !!(options && options.composed);
|
|
1552
|
+
var node = this;
|
|
1553
|
+
while (true) {
|
|
1554
|
+
var parent = node.parentNode;
|
|
1555
|
+
if (parent) { node = parent; continue; }
|
|
1556
|
+
if (composed && node.host) { node = node.host; continue; }
|
|
1557
|
+
return __nativeCanonicalizeRootNode(node);
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
Object.defineProperty(Node.prototype, 'isConnected', {
|
|
1562
|
+
get: function() { return this.getRootNode() === document; },
|
|
1563
|
+
enumerable: true,
|
|
1564
|
+
configurable: true,
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
function locateNamespace(node, prefix) {
|
|
1568
|
+
if (!node) return null;
|
|
1569
|
+
if (node.nodeType === 1) {
|
|
1570
|
+
if (node.namespaceURI !== null && node.prefix === prefix) return node.namespaceURI;
|
|
1571
|
+
var attrs = node.attributes;
|
|
1572
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
1573
|
+
var a = attrs[i];
|
|
1574
|
+
if (prefix === null && a.name === 'xmlns') return a.value || null;
|
|
1575
|
+
if (prefix !== null && a.name === 'xmlns:' + prefix) return a.value || null;
|
|
1576
|
+
}
|
|
1577
|
+
return locateNamespace(node.parentElement, prefix);
|
|
1578
|
+
}
|
|
1579
|
+
if (node.nodeType === 9) {
|
|
1580
|
+
return node.documentElement ? locateNamespace(node.documentElement, prefix) : null;
|
|
1581
|
+
}
|
|
1582
|
+
if (node.nodeType === 10 || node.nodeType === 11) return null;
|
|
1583
|
+
return locateNamespace(node.parentElement, prefix);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
Node.prototype.lookupNamespaceURI = function(prefix) {
|
|
1587
|
+
if (prefix === undefined || prefix === '') prefix = null;
|
|
1588
|
+
return locateNamespace(this, prefix);
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
Node.prototype.isDefaultNamespace = function(namespace) {
|
|
1592
|
+
if (namespace === undefined || namespace === '') namespace = null;
|
|
1593
|
+
return this.lookupNamespaceURI(null) === namespace;
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
Node.prototype.lookupPrefix = function(namespace) {
|
|
1597
|
+
if (!namespace) return null;
|
|
1598
|
+
var node = this.nodeType === 9 ? this.documentElement : this;
|
|
1599
|
+
while (node && node.nodeType === 1) {
|
|
1600
|
+
if (node.namespaceURI === namespace && node.prefix !== null) return node.prefix;
|
|
1601
|
+
var attrs = node.attributes;
|
|
1602
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
1603
|
+
var a = attrs[i];
|
|
1604
|
+
if (a.name.indexOf('xmlns:') === 0 && a.value === namespace) return a.name.slice(6);
|
|
1605
|
+
}
|
|
1606
|
+
node = node.parentElement;
|
|
1607
|
+
}
|
|
1608
|
+
return null;
|
|
1609
|
+
};
|
|
1610
|
+
})();
|
|
1611
|
+
)JS";
|
|
1612
|
+
JSValue root_node_result = JS_Eval(ctx, kGetRootNodeScript, strlen(kGetRootNodeScript),
|
|
1613
|
+
"<get-root-node-bootstrap>", JS_EVAL_TYPE_GLOBAL);
|
|
1614
|
+
if (JS_IsException(root_node_result)) JS_FreeValue(ctx, JS_GetException(ctx));
|
|
1615
|
+
JS_FreeValue(ctx, root_node_result);
|
|
1340
1616
|
}
|
|
1341
1617
|
|
|
1342
1618
|
} // namespace margelo::nitro::nitrojsdom
|