@salve-software/react-native-nitro-jsdom 1.1.0 → 2.1.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 +6 -0
- package/cpp/HybridHtmlSandbox.cpp +14 -7
- package/cpp/HybridHtmlSandbox.hpp +1 -1
- package/cpp/lexbor/LexborDocument.cpp +65 -3
- package/cpp/lexbor/LexborDocument.hpp +17 -2
- package/cpp/quickjs/DOMBindings.cpp +14 -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 +29 -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 +96 -0
- package/cpp/quickjs/bindings/CSSOMBindings.hpp +3 -1
- package/cpp/quickjs/bindings/CustomElementsBindings.cpp +1 -1
- package/cpp/quickjs/bindings/DOMParserBindings.cpp +299 -0
- package/cpp/quickjs/bindings/DOMParserBindings.hpp +45 -0
- package/cpp/quickjs/bindings/DocumentBindings.cpp +174 -11
- package/cpp/quickjs/bindings/ElementBindings.cpp +516 -20
- package/cpp/quickjs/bindings/EventBindings.cpp +281 -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 +294 -0
- package/cpp/quickjs/bindings/FormBindings.hpp +9 -5
- package/cpp/quickjs/bindings/IntlBindings.cpp +352 -0
- package/cpp/quickjs/bindings/IntlBindings.hpp +29 -0
- 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 +43 -0
- package/cpp/quickjs/bindings/WindowBindings.cpp +295 -4
- package/cpp/quickjs/bindings/WindowBindings.hpp +3 -2
- package/cpp/quickjs/bindings/WindowNamedPropertiesBindings.cpp +241 -0
- package/cpp/quickjs/bindings/WindowNamedPropertiesBindings.hpp +30 -0
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js +2 -1
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js.map +1 -1
- package/lib/module/classes/JSDOM/JSDOM.class.js +2 -1
- package/lib/module/classes/JSDOM/JSDOM.class.js.map +1 -1
- package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts.map +1 -1
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts +5 -4
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts.map +1 -1
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts +1 -1
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts.map +1 -1
- package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.hpp +1 -1
- package/package.json +17 -2
- package/src/classes/JSDOM/JSDOM.class.ts +2 -1
- package/src/classes/JSDOM/types/IJSDOMOptions.ts +5 -4
- package/src/specs/HtmlSandbox.nitro.ts +1 -1
|
@@ -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;
|
|
@@ -292,6 +303,107 @@ JSValue js_el_set_checked(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
292
303
|
return JS_UNDEFINED;
|
|
293
304
|
}
|
|
294
305
|
|
|
306
|
+
JSValue bool_attr_get(JSContext* ctx, JSValue this_val, const char* attr, size_t attr_len) {
|
|
307
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
308
|
+
if (!el) return JS_FALSE;
|
|
309
|
+
bool has = lxb_dom_element_has_attribute(el, reinterpret_cast<const lxb_char_t*>(attr), attr_len);
|
|
310
|
+
return JS_NewBool(ctx, has);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
JSValue bool_attr_set(JSContext* ctx, JSValue this_val, JSValue val, const char* attr, size_t attr_len) {
|
|
314
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
315
|
+
if (!el) return JS_UNDEFINED;
|
|
316
|
+
bool on = JS_ToBool(ctx, val) > 0;
|
|
317
|
+
auto* attr_name = reinterpret_cast<const lxb_char_t*>(attr);
|
|
318
|
+
bool has = lxb_dom_element_has_attribute(el, attr_name, attr_len);
|
|
319
|
+
|
|
320
|
+
auto* rctx = get_ctx(ctx);
|
|
321
|
+
bool has_obs = rctx && rctx->mutation_observers && !rctx->mutation_observers->empty();
|
|
322
|
+
std::optional<std::string> old_val;
|
|
323
|
+
if (has_obs && has && rctx->mutation_observers->hasAttributeOldValueObserver()) {
|
|
324
|
+
size_t len = 0;
|
|
325
|
+
const lxb_char_t* v = lxb_dom_element_get_attribute(el, attr_name, attr_len, &len);
|
|
326
|
+
if (v) old_val = std::string(reinterpret_cast<const char*>(v), len);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (on && !has) {
|
|
330
|
+
lxb_dom_element_set_attribute(el, attr_name, attr_len, reinterpret_cast<const lxb_char_t*>(""), 0);
|
|
331
|
+
if (has_obs) {
|
|
332
|
+
rctx->mutation_observers->notifyAttribute(ctx, lxb_dom_interface_node(el), attr, std::nullopt);
|
|
333
|
+
}
|
|
334
|
+
} else if (!on && has) {
|
|
335
|
+
lxb_dom_element_remove_attribute(el, attr_name, attr_len);
|
|
336
|
+
if (has_obs) {
|
|
337
|
+
rctx->mutation_observers->notifyAttribute(ctx, lxb_dom_interface_node(el), attr, old_val);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return JS_UNDEFINED;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
JSValue js_el_get_disabled(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "disabled", 8); }
|
|
345
|
+
JSValue js_el_set_disabled(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "disabled", 8); }
|
|
346
|
+
|
|
347
|
+
JSValue js_el_get_required(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "required", 8); }
|
|
348
|
+
JSValue js_el_set_required(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "required", 8); }
|
|
349
|
+
|
|
350
|
+
JSValue js_el_get_readOnly(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "readonly", 8); }
|
|
351
|
+
JSValue js_el_set_readOnly(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "readonly", 8); }
|
|
352
|
+
|
|
353
|
+
JSValue js_el_get_multiple(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "multiple", 8); }
|
|
354
|
+
JSValue js_el_set_multiple(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "multiple", 8); }
|
|
355
|
+
|
|
356
|
+
JSValue js_el_get_autofocus(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "autofocus", 9); }
|
|
357
|
+
JSValue js_el_set_autofocus(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "autofocus", 9); }
|
|
358
|
+
|
|
359
|
+
JSValue js_el_get_selected(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "selected", 8); }
|
|
360
|
+
JSValue js_el_set_selected(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "selected", 8); }
|
|
361
|
+
|
|
362
|
+
JSValue js_el_get_hidden(JSContext* ctx, JSValue this_val) { return bool_attr_get(ctx, this_val, "hidden", 6); }
|
|
363
|
+
JSValue js_el_set_hidden(JSContext* ctx, JSValue this_val, JSValue val) { return bool_attr_set(ctx, this_val, val, "hidden", 6); }
|
|
364
|
+
|
|
365
|
+
JSValue string_attr_get(JSContext* ctx, JSValue this_val, const char* attr, size_t attr_len) {
|
|
366
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
367
|
+
if (!el) return JS_NewString(ctx, "");
|
|
368
|
+
size_t len = 0;
|
|
369
|
+
const lxb_char_t* val = lxb_dom_element_get_attribute(el, reinterpret_cast<const lxb_char_t*>(attr), attr_len, &len);
|
|
370
|
+
return val ? JS_NewStringLen(ctx, reinterpret_cast<const char*>(val), len) : JS_NewString(ctx, "");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
JSValue string_attr_set(JSContext* ctx, JSValue this_val, JSValue val, const char* attr, size_t attr_len) {
|
|
374
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
375
|
+
if (!el) return JS_UNDEFINED;
|
|
376
|
+
const char* str = JS_ToCString(ctx, val);
|
|
377
|
+
if (!str) return JS_UNDEFINED;
|
|
378
|
+
auto* attr_name = reinterpret_cast<const lxb_char_t*>(attr);
|
|
379
|
+
|
|
380
|
+
auto* rctx = get_ctx(ctx);
|
|
381
|
+
bool has_obs = rctx && rctx->mutation_observers && !rctx->mutation_observers->empty();
|
|
382
|
+
std::optional<std::string> old_val;
|
|
383
|
+
if (has_obs && rctx->mutation_observers->hasAttributeOldValueObserver()) {
|
|
384
|
+
size_t len = 0;
|
|
385
|
+
const lxb_char_t* v = lxb_dom_element_get_attribute(el, attr_name, attr_len, &len);
|
|
386
|
+
if (v) old_val = std::string(reinterpret_cast<const char*>(v), len);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
lxb_dom_element_set_attribute(el, attr_name, attr_len, reinterpret_cast<const lxb_char_t*>(str), strlen(str));
|
|
390
|
+
JS_FreeCString(ctx, str);
|
|
391
|
+
|
|
392
|
+
if (has_obs) {
|
|
393
|
+
rctx->mutation_observers->notifyAttribute(ctx, lxb_dom_interface_node(el), attr, old_val);
|
|
394
|
+
}
|
|
395
|
+
return JS_UNDEFINED;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
JSValue js_el_get_title(JSContext* ctx, JSValue this_val) { return string_attr_get(ctx, this_val, "title", 5); }
|
|
399
|
+
JSValue js_el_set_title(JSContext* ctx, JSValue this_val, JSValue val) { return string_attr_set(ctx, this_val, val, "title", 5); }
|
|
400
|
+
|
|
401
|
+
JSValue js_el_get_lang(JSContext* ctx, JSValue this_val) { return string_attr_get(ctx, this_val, "lang", 4); }
|
|
402
|
+
JSValue js_el_set_lang(JSContext* ctx, JSValue this_val, JSValue val) { return string_attr_set(ctx, this_val, val, "lang", 4); }
|
|
403
|
+
|
|
404
|
+
JSValue js_el_get_dir(JSContext* ctx, JSValue this_val) { return string_attr_get(ctx, this_val, "dir", 3); }
|
|
405
|
+
JSValue js_el_set_dir(JSContext* ctx, JSValue this_val, JSValue val) { return string_attr_set(ctx, this_val, val, "dir", 3); }
|
|
406
|
+
|
|
295
407
|
JSValue js_el_get_textContent(JSContext* ctx, JSValue this_val) {
|
|
296
408
|
lxb_dom_node_t* node = unwrap_node(ctx, this_val);
|
|
297
409
|
if (!node) return JS_NewString(ctx, "");
|
|
@@ -340,7 +452,7 @@ JSValue js_el_set_textContent(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
340
452
|
if (has_observers) rctx->mutation_observers->disconnectDetachedTargets(old_children);
|
|
341
453
|
}
|
|
342
454
|
}
|
|
343
|
-
|
|
455
|
+
doc_for_node(ctx, node)->setTextContentOnEl(el, str);
|
|
344
456
|
if (has_observers) {
|
|
345
457
|
std::vector<void*> new_children;
|
|
346
458
|
lxb_dom_node_t* child = node->first_child;
|
|
@@ -385,7 +497,7 @@ JSValue js_el_set_innerHTML(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
385
497
|
if (has_observers) rctx->mutation_observers->disconnectDetachedTargets(old_children);
|
|
386
498
|
}
|
|
387
499
|
|
|
388
|
-
|
|
500
|
+
doc_for_node(ctx, el)->setInnerHTMLOnEl(el, html);
|
|
389
501
|
JS_FreeCString(ctx, html);
|
|
390
502
|
|
|
391
503
|
if (has_observers) {
|
|
@@ -530,6 +642,16 @@ JSValue js_el_set_nodeValue(JSContext* ctx, JSValue this_val, JSValue val) {
|
|
|
530
642
|
return JS_UNDEFINED;
|
|
531
643
|
}
|
|
532
644
|
|
|
645
|
+
JSValue js_el_get_data_length(JSContext* ctx, JSValue this_val) {
|
|
646
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
647
|
+
if (!node) return JS_UNDEFINED;
|
|
648
|
+
if (node->type != LXB_DOM_NODE_TYPE_TEXT && node->type != LXB_DOM_NODE_TYPE_COMMENT) return JS_UNDEFINED;
|
|
649
|
+
JSValue str_val = js_el_get_nodeValue(ctx, this_val);
|
|
650
|
+
JSValue len_val = JS_GetPropertyStr(ctx, str_val, "length");
|
|
651
|
+
JS_FreeValue(ctx, str_val);
|
|
652
|
+
return len_val;
|
|
653
|
+
}
|
|
654
|
+
|
|
533
655
|
JSValue js_el_get_childNodes(JSContext* ctx, JSValue this_val) {
|
|
534
656
|
auto* node = unwrap_node(ctx, this_val);
|
|
535
657
|
if (!node) return JS_NewArray(ctx);
|
|
@@ -651,6 +773,93 @@ JSValue js_el_hasAttribute(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
651
773
|
return JS_NewBool(ctx, has);
|
|
652
774
|
}
|
|
653
775
|
|
|
776
|
+
// ── Namespaced attribute methods (getAttributeNS/setAttributeNS/...) ─────────
|
|
777
|
+
// Simplification: this sandbox doesn't track a real (namespace, localName)
|
|
778
|
+
// identity per attribute the way createElementNS()/namespaceURI() do for
|
|
779
|
+
// elements — building that would mean hand-constructing lxb_dom_attr_t nodes
|
|
780
|
+
// with their own ns/prefix fields, a much larger and riskier lift than the
|
|
781
|
+
// element side. Instead, attributes are matched by local name only (the
|
|
782
|
+
// qualified name with any "prefix:" stripped), which is spec-accurate for
|
|
783
|
+
// the common real-world case (a single xlink:href-style attribute) but not
|
|
784
|
+
// for two attributes sharing a local name across different namespaces — a
|
|
785
|
+
// case that essentially never comes up in embedded-widget scripts.
|
|
786
|
+
lxb_dom_attr_t* find_attr_by_local_name(lxb_dom_element_t* el, const std::string& localName) {
|
|
787
|
+
for (lxb_dom_attr_t* attr = lxb_dom_element_first_attribute(el); attr; attr = lxb_dom_element_next_attribute(attr)) {
|
|
788
|
+
size_t len = 0;
|
|
789
|
+
const lxb_char_t* qname = lxb_dom_attr_qualified_name(attr, &len);
|
|
790
|
+
if (!qname) continue;
|
|
791
|
+
std::string q(reinterpret_cast<const char*>(qname), len);
|
|
792
|
+
auto colon = q.find(':');
|
|
793
|
+
std::string local = (colon == std::string::npos) ? q : q.substr(colon + 1);
|
|
794
|
+
if (local == localName) return attr;
|
|
795
|
+
}
|
|
796
|
+
return nullptr;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
JSValue js_el_getAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
800
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
801
|
+
if (!el || argc < 2) return JS_NULL;
|
|
802
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
803
|
+
if (!local) return JS_NULL;
|
|
804
|
+
auto* attr = find_attr_by_local_name(el, local);
|
|
805
|
+
JS_FreeCString(ctx, local);
|
|
806
|
+
if (!attr) return JS_NULL;
|
|
807
|
+
size_t len = 0;
|
|
808
|
+
const lxb_char_t* val = lxb_dom_attr_value(attr, &len);
|
|
809
|
+
return val ? JS_NewStringLen(ctx, reinterpret_cast<const char*>(val), len) : JS_NewString(ctx, "");
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
JSValue js_el_hasAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
813
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
814
|
+
if (!el || argc < 2) return JS_FALSE;
|
|
815
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
816
|
+
if (!local) return JS_FALSE;
|
|
817
|
+
bool has = find_attr_by_local_name(el, local) != nullptr;
|
|
818
|
+
JS_FreeCString(ctx, local);
|
|
819
|
+
return JS_NewBool(ctx, has);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
JSValue js_el_setAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
823
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
824
|
+
if (!el || argc < 3) return JS_UNDEFINED;
|
|
825
|
+
const char* qualified_name = JS_ToCString(ctx, argv[1]);
|
|
826
|
+
const char* value = JS_ToCString(ctx, argv[2]);
|
|
827
|
+
if (qualified_name && value) {
|
|
828
|
+
std::string qn(qualified_name);
|
|
829
|
+
auto colon = qn.find(':');
|
|
830
|
+
std::string local = (colon == std::string::npos) ? qn : qn.substr(colon + 1);
|
|
831
|
+
|
|
832
|
+
auto* existing = find_attr_by_local_name(el, local);
|
|
833
|
+
if (existing) {
|
|
834
|
+
lxb_dom_attr_set_existing_value(existing,
|
|
835
|
+
reinterpret_cast<const lxb_char_t*>(value), strlen(value));
|
|
836
|
+
} else {
|
|
837
|
+
lxb_dom_element_set_attribute(el,
|
|
838
|
+
reinterpret_cast<const lxb_char_t*>(qualified_name), qn.size(),
|
|
839
|
+
reinterpret_cast<const lxb_char_t*>(value), strlen(value));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (qualified_name) JS_FreeCString(ctx, qualified_name);
|
|
843
|
+
if (value) JS_FreeCString(ctx, value);
|
|
844
|
+
return JS_UNDEFINED;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
JSValue js_el_removeAttributeNS(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
848
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
849
|
+
if (!el || argc < 2) return JS_UNDEFINED;
|
|
850
|
+
const char* local = JS_ToCString(ctx, argv[1]);
|
|
851
|
+
if (local) {
|
|
852
|
+
auto* attr = find_attr_by_local_name(el, local);
|
|
853
|
+
if (attr) {
|
|
854
|
+
size_t qlen = 0;
|
|
855
|
+
const lxb_char_t* qname = lxb_dom_attr_qualified_name(attr, &qlen);
|
|
856
|
+
if (qname) lxb_dom_element_remove_attribute(el, qname, qlen);
|
|
857
|
+
}
|
|
858
|
+
JS_FreeCString(ctx, local);
|
|
859
|
+
}
|
|
860
|
+
return JS_UNDEFINED;
|
|
861
|
+
}
|
|
862
|
+
|
|
654
863
|
// Inserts `node` via `insert_one`, expanding DocumentFragments into their
|
|
655
864
|
// children first (per the DOM's "insert a node" algorithm — a fragment is
|
|
656
865
|
// never itself part of the resulting tree, only its children are moved in).
|
|
@@ -821,12 +1030,15 @@ JSValue js_el_replaceChild(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
821
1030
|
return JS_DupValue(ctx, argv[1]); // returns the replaced (old) child, per spec
|
|
822
1031
|
}
|
|
823
1032
|
|
|
824
|
-
|
|
1033
|
+
// `context_node` supplies the owning document for a string argument coerced
|
|
1034
|
+
// into a new text node (see doc_for_node()) — irrelevant when `val` is
|
|
1035
|
+
// already a node.
|
|
1036
|
+
lxb_dom_node_t* js_to_node_or_text(JSContext* ctx, JSValue val, lxb_dom_node_t* context_node) {
|
|
825
1037
|
auto* node = unwrap_node(ctx, val);
|
|
826
1038
|
if (node) return node;
|
|
827
1039
|
const char* str = JS_ToCString(ctx, val);
|
|
828
1040
|
if (!str) return nullptr;
|
|
829
|
-
void* text =
|
|
1041
|
+
void* text = doc_for_node(ctx, context_node)->createTextNode(str);
|
|
830
1042
|
JS_FreeCString(ctx, str);
|
|
831
1043
|
return static_cast<lxb_dom_node_t*>(text);
|
|
832
1044
|
}
|
|
@@ -838,7 +1050,7 @@ JSValue js_el_before(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
838
1050
|
|
|
839
1051
|
std::vector<void*> inserted;
|
|
840
1052
|
for (int i = 0; i < argc; i++) {
|
|
841
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1053
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
842
1054
|
if (!n) continue;
|
|
843
1055
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_before(node, one); });
|
|
844
1056
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -860,7 +1072,7 @@ JSValue js_el_after(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
|
860
1072
|
|
|
861
1073
|
std::vector<void*> inserted;
|
|
862
1074
|
for (int i = 0; i < argc; i++) {
|
|
863
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1075
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
864
1076
|
if (!n) continue;
|
|
865
1077
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) {
|
|
866
1078
|
if (ref) lxb_dom_node_insert_before(ref, one); else lxb_dom_node_insert_child(parent, one);
|
|
@@ -884,7 +1096,7 @@ JSValue js_el_replaceWith(JSContext* ctx, JSValue this_val, int argc, JSValue* a
|
|
|
884
1096
|
|
|
885
1097
|
std::vector<void*> inserted;
|
|
886
1098
|
for (int i = 0; i < argc; i++) {
|
|
887
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1099
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], node);
|
|
888
1100
|
if (!n) continue;
|
|
889
1101
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_before(node, one); });
|
|
890
1102
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -906,7 +1118,7 @@ JSValue js_el_append(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
906
1118
|
|
|
907
1119
|
std::vector<void*> inserted;
|
|
908
1120
|
for (int i = 0; i < argc; i++) {
|
|
909
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1121
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], parent_node);
|
|
910
1122
|
if (!n) continue;
|
|
911
1123
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) { lxb_dom_node_insert_child(parent_node, one); });
|
|
912
1124
|
inserted.insert(inserted.end(), batch.begin(), batch.end());
|
|
@@ -927,7 +1139,7 @@ JSValue js_el_prepend(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
927
1139
|
|
|
928
1140
|
std::vector<void*> inserted;
|
|
929
1141
|
for (int i = 0; i < argc; i++) {
|
|
930
|
-
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i]);
|
|
1142
|
+
lxb_dom_node_t* n = js_to_node_or_text(ctx, argv[i], parent_node);
|
|
931
1143
|
if (!n) continue;
|
|
932
1144
|
auto batch = expand_and_insert(n, [&](lxb_dom_node_t* one) {
|
|
933
1145
|
if (ref) lxb_dom_node_insert_before(ref, one); else lxb_dom_node_insert_child(parent_node, one);
|
|
@@ -948,11 +1160,12 @@ JSValue js_el_closest(JSContext* ctx, JSValue this_val, int argc, JSValue* argv)
|
|
|
948
1160
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
949
1161
|
if (!sel) return JS_NULL;
|
|
950
1162
|
|
|
1163
|
+
auto* doc = doc_for_node(ctx, lxb_dom_interface_node(el));
|
|
951
1164
|
JSValue result = JS_NULL;
|
|
952
1165
|
for (lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
953
1166
|
node && node->type == LXB_DOM_NODE_TYPE_ELEMENT;
|
|
954
1167
|
node = node->parent) {
|
|
955
|
-
if (
|
|
1168
|
+
if (doc->matchesSelector(lxb_dom_interface_element(node), sel)) {
|
|
956
1169
|
result = make_element(ctx, node);
|
|
957
1170
|
break;
|
|
958
1171
|
}
|
|
@@ -975,7 +1188,7 @@ JSValue js_el_insertAdjacentHTML(JSContext* ctx, JSValue this_val, int argc, JSV
|
|
|
975
1188
|
JS_FreeCString(ctx, position);
|
|
976
1189
|
|
|
977
1190
|
lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
978
|
-
auto parsed =
|
|
1191
|
+
auto parsed = doc_for_node(ctx, node)->parseFragmentNodes(el, html);
|
|
979
1192
|
JS_FreeCString(ctx, html);
|
|
980
1193
|
if (parsed.empty()) return JS_UNDEFINED;
|
|
981
1194
|
|
|
@@ -1023,12 +1236,119 @@ JSValue js_el_insertAdjacentHTML(JSContext* ctx, JSValue this_val, int argc, JSV
|
|
|
1023
1236
|
return JS_UNDEFINED;
|
|
1024
1237
|
}
|
|
1025
1238
|
|
|
1239
|
+
JSValue js_el_insertAdjacentElement(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1240
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1241
|
+
if (!el || argc < 2) return JS_NULL;
|
|
1242
|
+
const char* position = JS_ToCString(ctx, argv[0]);
|
|
1243
|
+
if (!position) return JS_NULL;
|
|
1244
|
+
std::string pos(position);
|
|
1245
|
+
JS_FreeCString(ctx, position);
|
|
1246
|
+
|
|
1247
|
+
auto* inserted_el = unwrap_element(ctx, argv[1]);
|
|
1248
|
+
if (!inserted_el) return JS_NULL;
|
|
1249
|
+
lxb_dom_node_t* inserted = lxb_dom_interface_node(inserted_el);
|
|
1250
|
+
|
|
1251
|
+
lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
1252
|
+
lxb_dom_node_t* parent = nullptr;
|
|
1253
|
+
lxb_dom_node_t* prev_sib = nullptr;
|
|
1254
|
+
lxb_dom_node_t* next_sib = nullptr;
|
|
1255
|
+
|
|
1256
|
+
if (pos == "beforebegin") {
|
|
1257
|
+
parent = node->parent;
|
|
1258
|
+
if (!parent) return JS_NULL;
|
|
1259
|
+
prev_sib = node->prev;
|
|
1260
|
+
lxb_dom_node_insert_before(node, inserted);
|
|
1261
|
+
next_sib = node;
|
|
1262
|
+
} else if (pos == "afterbegin") {
|
|
1263
|
+
parent = node;
|
|
1264
|
+
lxb_dom_node_t* ref = node->first_child;
|
|
1265
|
+
if (ref) lxb_dom_node_insert_before(ref, inserted); else lxb_dom_node_insert_child(node, inserted);
|
|
1266
|
+
next_sib = ref;
|
|
1267
|
+
} else if (pos == "beforeend") {
|
|
1268
|
+
parent = node;
|
|
1269
|
+
prev_sib = node->last_child;
|
|
1270
|
+
lxb_dom_node_insert_child(node, inserted);
|
|
1271
|
+
} else if (pos == "afterend") {
|
|
1272
|
+
parent = node->parent;
|
|
1273
|
+
if (!parent) return JS_NULL;
|
|
1274
|
+
prev_sib = node;
|
|
1275
|
+
lxb_dom_node_t* ref = node->next;
|
|
1276
|
+
if (ref) lxb_dom_node_insert_before(ref, inserted); else lxb_dom_node_insert_child(parent, inserted);
|
|
1277
|
+
next_sib = ref;
|
|
1278
|
+
} else {
|
|
1279
|
+
return throw_dom_exception(ctx, "SyntaxError", ("invalid insertAdjacentElement position '" + pos + "'").c_str());
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
auto* rctx = get_ctx(ctx);
|
|
1283
|
+
if (rctx && rctx->mutation_observers && !rctx->mutation_observers->empty()) {
|
|
1284
|
+
rctx->mutation_observers->notifyChildList(ctx, parent, {inserted}, {}, prev_sib, next_sib);
|
|
1285
|
+
}
|
|
1286
|
+
return JS_DupValue(ctx, argv[1]);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
JSValue js_el_insertAdjacentText(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1290
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1291
|
+
if (!el || argc < 2) return JS_UNDEFINED;
|
|
1292
|
+
const char* position = JS_ToCString(ctx, argv[0]);
|
|
1293
|
+
const char* text = JS_ToCString(ctx, argv[1]);
|
|
1294
|
+
if (!position || !text) {
|
|
1295
|
+
if (position) JS_FreeCString(ctx, position);
|
|
1296
|
+
if (text) JS_FreeCString(ctx, text);
|
|
1297
|
+
return JS_UNDEFINED;
|
|
1298
|
+
}
|
|
1299
|
+
std::string pos(position);
|
|
1300
|
+
JS_FreeCString(ctx, position);
|
|
1301
|
+
|
|
1302
|
+
lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
1303
|
+
void* text_node_v = doc_for_node(ctx, node)->createTextNode(text);
|
|
1304
|
+
JS_FreeCString(ctx, text);
|
|
1305
|
+
if (!text_node_v) return JS_UNDEFINED;
|
|
1306
|
+
auto* text_node = static_cast<lxb_dom_node_t*>(text_node_v);
|
|
1307
|
+
|
|
1308
|
+
lxb_dom_node_t* parent = nullptr;
|
|
1309
|
+
lxb_dom_node_t* prev_sib = nullptr;
|
|
1310
|
+
lxb_dom_node_t* next_sib = nullptr;
|
|
1311
|
+
|
|
1312
|
+
if (pos == "beforebegin") {
|
|
1313
|
+
parent = node->parent;
|
|
1314
|
+
if (!parent) { lxb_dom_node_destroy_deep(text_node); return JS_UNDEFINED; }
|
|
1315
|
+
prev_sib = node->prev;
|
|
1316
|
+
lxb_dom_node_insert_before(node, text_node);
|
|
1317
|
+
next_sib = node;
|
|
1318
|
+
} else if (pos == "afterbegin") {
|
|
1319
|
+
parent = node;
|
|
1320
|
+
lxb_dom_node_t* ref = node->first_child;
|
|
1321
|
+
if (ref) lxb_dom_node_insert_before(ref, text_node); else lxb_dom_node_insert_child(node, text_node);
|
|
1322
|
+
next_sib = ref;
|
|
1323
|
+
} else if (pos == "beforeend") {
|
|
1324
|
+
parent = node;
|
|
1325
|
+
prev_sib = node->last_child;
|
|
1326
|
+
lxb_dom_node_insert_child(node, text_node);
|
|
1327
|
+
} else if (pos == "afterend") {
|
|
1328
|
+
parent = node->parent;
|
|
1329
|
+
if (!parent) { lxb_dom_node_destroy_deep(text_node); return JS_UNDEFINED; }
|
|
1330
|
+
prev_sib = node;
|
|
1331
|
+
lxb_dom_node_t* ref = node->next;
|
|
1332
|
+
if (ref) lxb_dom_node_insert_before(ref, text_node); else lxb_dom_node_insert_child(parent, text_node);
|
|
1333
|
+
next_sib = ref;
|
|
1334
|
+
} else {
|
|
1335
|
+
lxb_dom_node_destroy_deep(text_node);
|
|
1336
|
+
return throw_dom_exception(ctx, "SyntaxError", ("invalid insertAdjacentText position '" + pos + "'").c_str());
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
auto* rctx = get_ctx(ctx);
|
|
1340
|
+
if (rctx && rctx->mutation_observers && !rctx->mutation_observers->empty()) {
|
|
1341
|
+
rctx->mutation_observers->notifyChildList(ctx, parent, {text_node}, {}, prev_sib, next_sib);
|
|
1342
|
+
}
|
|
1343
|
+
return JS_UNDEFINED;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1026
1346
|
JSValue js_el_matches(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1027
1347
|
auto* el = unwrap_element(ctx, this_val);
|
|
1028
1348
|
if (!el || argc < 1) return JS_FALSE;
|
|
1029
1349
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1030
1350
|
if (!sel) return JS_FALSE;
|
|
1031
|
-
bool result =
|
|
1351
|
+
bool result = doc_for_node(ctx, el)->matchesSelector(el, sel);
|
|
1032
1352
|
JS_FreeCString(ctx, sel);
|
|
1033
1353
|
return JS_NewBool(ctx, result);
|
|
1034
1354
|
}
|
|
@@ -1038,7 +1358,7 @@ JSValue js_el_querySelector(JSContext* ctx, JSValue this_val, int argc, JSValue*
|
|
|
1038
1358
|
if (!el || argc < 1) return JS_NULL;
|
|
1039
1359
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1040
1360
|
if (!sel) return JS_NULL;
|
|
1041
|
-
void* found =
|
|
1361
|
+
void* found = doc_for_node(ctx, el)->querySelectorFromEl(el, sel);
|
|
1042
1362
|
JS_FreeCString(ctx, sel);
|
|
1043
1363
|
return make_element(ctx, found);
|
|
1044
1364
|
}
|
|
@@ -1048,7 +1368,7 @@ JSValue js_el_querySelectorAll(JSContext* ctx, JSValue this_val, int argc, JSVal
|
|
|
1048
1368
|
if (!el || argc < 1) return JS_NewArray(ctx);
|
|
1049
1369
|
const char* sel = JS_ToCString(ctx, argv[0]);
|
|
1050
1370
|
if (!sel) return JS_NewArray(ctx);
|
|
1051
|
-
auto results =
|
|
1371
|
+
auto results = doc_for_node(ctx, el)->querySelectorAllFromEl(el, sel);
|
|
1052
1372
|
JS_FreeCString(ctx, sel);
|
|
1053
1373
|
return make_element_array(ctx, results);
|
|
1054
1374
|
}
|
|
@@ -1154,6 +1474,14 @@ JSValue js_el_getBoundingClientRect(JSContext* ctx, JSValue this_val, int, JSVal
|
|
|
1154
1474
|
return rect;
|
|
1155
1475
|
}
|
|
1156
1476
|
|
|
1477
|
+
JSValue js_el_getClientRects(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1478
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1479
|
+
if (!el) return JS_NewArray(ctx);
|
|
1480
|
+
JSValue arr = JS_NewArray(ctx);
|
|
1481
|
+
JS_SetPropertyUint32(ctx, arr, 0, js_el_getBoundingClientRect(ctx, this_val, argc, argv));
|
|
1482
|
+
return arr;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1157
1485
|
JSValue js_el_isSameNode(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
|
|
1158
1486
|
auto* node = unwrap_node(ctx, this_val);
|
|
1159
1487
|
if (!node || argc < 1) return JS_FALSE;
|
|
@@ -1229,7 +1557,7 @@ JSValue js_el_normalize(JSContext* ctx, JSValue this_val, int, JSValue*) {
|
|
|
1229
1557
|
invalidate_node_cache_batch(ctx, get_ctx(ctx), text_nodes);
|
|
1230
1558
|
}
|
|
1231
1559
|
|
|
1232
|
-
|
|
1560
|
+
doc_for_node(ctx, node)->normalize(node);
|
|
1233
1561
|
return JS_UNDEFINED;
|
|
1234
1562
|
}
|
|
1235
1563
|
|
|
@@ -1238,7 +1566,75 @@ JSValue js_el_compareDocumentPosition(JSContext* ctx, JSValue this_val, int argc
|
|
|
1238
1566
|
if (!node || argc < 1) return JS_NewInt32(ctx, 0);
|
|
1239
1567
|
auto* other = unwrap_node(ctx, argv[0]);
|
|
1240
1568
|
if (!other) return JS_NewInt32(ctx, 0);
|
|
1241
|
-
return JS_NewInt32(ctx,
|
|
1569
|
+
return JS_NewInt32(ctx, doc_for_node(ctx, node)->compareDocumentPosition(node, other));
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
JSValue js_canonicalize_root_node(JSContext* ctx, JSValue, int argc, JSValue* argv) {
|
|
1573
|
+
if (argc < 1) return JS_UNDEFINED;
|
|
1574
|
+
auto* node = unwrap_node(ctx, argv[0]);
|
|
1575
|
+
if (node && node->type == LXB_DOM_NODE_TYPE_DOCUMENT && doc_for_node(ctx, node) == get_doc(ctx)) {
|
|
1576
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1577
|
+
JSValue doc_val = JS_GetPropertyStr(ctx, global, "document");
|
|
1578
|
+
JS_FreeValue(ctx, global);
|
|
1579
|
+
return doc_val;
|
|
1580
|
+
}
|
|
1581
|
+
return JS_DupValue(ctx, argv[0]);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
JSValue js_el_get_ownerDocument(JSContext* ctx, JSValue this_val) {
|
|
1585
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
1586
|
+
if (!node) return JS_NULL;
|
|
1587
|
+
if (node->type == LXB_DOM_NODE_TYPE_DOCUMENT) return JS_NULL;
|
|
1588
|
+
|
|
1589
|
+
LexborDocument* owner = doc_for_node(ctx, node);
|
|
1590
|
+
if (owner == get_doc(ctx)) {
|
|
1591
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1592
|
+
JSValue doc_val = JS_GetPropertyStr(ctx, global, "document");
|
|
1593
|
+
JS_FreeValue(ctx, global);
|
|
1594
|
+
return doc_val;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
JSValue wrapper = get_document_wrapper(ctx, owner);
|
|
1598
|
+
if (!JS_IsUndefined(wrapper)) return wrapper;
|
|
1599
|
+
|
|
1600
|
+
void* html_doc = owner->documentHtmlPtr();
|
|
1601
|
+
void* doc_node = lxb_dom_interface_node(
|
|
1602
|
+
lxb_dom_interface_document(static_cast<lxb_html_document_t*>(html_doc)));
|
|
1603
|
+
return make_element(ctx, doc_node);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
JSValue js_el_get_namespaceURI(JSContext* ctx, JSValue this_val) {
|
|
1607
|
+
auto* node = unwrap_node(ctx, this_val);
|
|
1608
|
+
if (!node) return JS_NULL;
|
|
1609
|
+
LexborDocument* owner = doc_for_node(ctx, node);
|
|
1610
|
+
std::string uri = owner ? owner->namespaceURI(node) : "";
|
|
1611
|
+
if (uri.empty()) return JS_NULL;
|
|
1612
|
+
return JS_NewStringLen(ctx, uri.data(), uri.size());
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// Element.prefix — the namespace prefix set via createElementNS('ns', 'prefix:local'),
|
|
1616
|
+
// or null for an element with no prefix (the overwhelmingly common case).
|
|
1617
|
+
JSValue js_el_get_prefix(JSContext* ctx, JSValue this_val) {
|
|
1618
|
+
auto* el = unwrap_element(ctx, this_val);
|
|
1619
|
+
if (!el) return JS_NULL;
|
|
1620
|
+
auto* node = reinterpret_cast<lxb_dom_node_t*>(el);
|
|
1621
|
+
if (node->prefix == 0) return JS_NULL;
|
|
1622
|
+
const lxb_ns_prefix_data_t* data = lxb_ns_prefix_data_by_id(node->owner_document->prefix, node->prefix);
|
|
1623
|
+
if (!data) return JS_NULL;
|
|
1624
|
+
const lxb_char_t* str = lexbor_hash_entry_str(&data->entry);
|
|
1625
|
+
if (!str) return JS_NULL;
|
|
1626
|
+
return JS_NewStringLen(ctx, reinterpret_cast<const char*>(str), data->entry.length);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// Node.baseURI — always the document's URL in this sandbox (no <base> element
|
|
1630
|
+
// support/per-node override), so this just forwards to location.href.
|
|
1631
|
+
JSValue js_el_get_baseURI(JSContext* ctx, JSValue) {
|
|
1632
|
+
JSValue global = JS_GetGlobalObject(ctx);
|
|
1633
|
+
JSValue location = JS_GetPropertyStr(ctx, global, "location");
|
|
1634
|
+
JS_FreeValue(ctx, global);
|
|
1635
|
+
JSValue href = JS_GetPropertyStr(ctx, location, "href");
|
|
1636
|
+
JS_FreeValue(ctx, location);
|
|
1637
|
+
return href;
|
|
1242
1638
|
}
|
|
1243
1639
|
|
|
1244
1640
|
} // namespace
|
|
@@ -1273,7 +1669,12 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1273
1669
|
define_prop(ctx, node_proto, "nodeType", js_el_get_nodeType, nullptr);
|
|
1274
1670
|
define_prop(ctx, node_proto, "nodeName", js_el_get_nodeName, nullptr);
|
|
1275
1671
|
define_prop(ctx, node_proto, "nodeValue", js_el_get_nodeValue, js_el_set_nodeValue);
|
|
1672
|
+
define_prop(ctx, node_proto, "data", js_el_get_nodeValue, js_el_set_nodeValue);
|
|
1673
|
+
define_prop(ctx, node_proto, "length", js_el_get_data_length, nullptr);
|
|
1276
1674
|
define_prop(ctx, node_proto, "textContent", js_el_get_textContent, js_el_set_textContent);
|
|
1675
|
+
define_prop(ctx, node_proto, "ownerDocument", js_el_get_ownerDocument, nullptr);
|
|
1676
|
+
define_prop(ctx, node_proto, "namespaceURI", js_el_get_namespaceURI, nullptr);
|
|
1677
|
+
define_prop(ctx, node_proto, "baseURI", js_el_get_baseURI, nullptr);
|
|
1277
1678
|
define_prop(ctx, node_proto, "childNodes", js_el_get_childNodes, nullptr);
|
|
1278
1679
|
define_prop(ctx, node_proto, "firstChild", js_el_get_firstChild, nullptr);
|
|
1279
1680
|
define_prop(ctx, node_proto, "lastChild", js_el_get_lastChild, nullptr);
|
|
@@ -1282,6 +1683,8 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1282
1683
|
define_prop(ctx, node_proto, "parentNode", js_el_get_parentNode, nullptr);
|
|
1283
1684
|
define_prop(ctx, node_proto, "parentElement", js_el_get_parentElement, nullptr);
|
|
1284
1685
|
|
|
1686
|
+
define_node_type_constants(ctx, node_proto);
|
|
1687
|
+
|
|
1285
1688
|
JS_SetClassProto(ctx, js_node_class_id, JS_DupValue(ctx, node_proto));
|
|
1286
1689
|
|
|
1287
1690
|
// ── Element proto: inherits Node, adds Element-specific APIs ─────────────
|
|
@@ -1292,27 +1695,47 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1292
1695
|
JS_SetPropertyStr(ctx, proto, "setAttribute", JS_NewCFunction(ctx, js_el_setAttribute, "setAttribute", 2));
|
|
1293
1696
|
JS_SetPropertyStr(ctx, proto, "removeAttribute", JS_NewCFunction(ctx, js_el_removeAttribute, "removeAttribute", 1));
|
|
1294
1697
|
JS_SetPropertyStr(ctx, proto, "hasAttribute", JS_NewCFunction(ctx, js_el_hasAttribute, "hasAttribute", 1));
|
|
1698
|
+
JS_SetPropertyStr(ctx, proto, "getAttributeNS", JS_NewCFunction(ctx, js_el_getAttributeNS, "getAttributeNS", 2));
|
|
1699
|
+
JS_SetPropertyStr(ctx, proto, "setAttributeNS", JS_NewCFunction(ctx, js_el_setAttributeNS, "setAttributeNS", 3));
|
|
1700
|
+
JS_SetPropertyStr(ctx, proto, "removeAttributeNS", JS_NewCFunction(ctx, js_el_removeAttributeNS, "removeAttributeNS", 2));
|
|
1701
|
+
JS_SetPropertyStr(ctx, proto, "hasAttributeNS", JS_NewCFunction(ctx, js_el_hasAttributeNS, "hasAttributeNS", 2));
|
|
1295
1702
|
JS_SetPropertyStr(ctx, proto, "toggleAttribute", JS_NewCFunction(ctx, js_el_toggleAttribute, "toggleAttribute", 2));
|
|
1296
1703
|
JS_SetPropertyStr(ctx, proto, "getAttributeNames", JS_NewCFunction(ctx, js_el_getAttributeNames, "getAttributeNames", 0));
|
|
1297
1704
|
JS_SetPropertyStr(ctx, proto, "remove", JS_NewCFunction(ctx, js_el_remove, "remove", 0));
|
|
1298
1705
|
JS_SetPropertyStr(ctx, proto, "matches", JS_NewCFunction(ctx, js_el_matches, "matches", 1));
|
|
1706
|
+
JS_SetPropertyStr(ctx, proto, "webkitMatchesSelector", JS_NewCFunction(ctx, js_el_matches, "webkitMatchesSelector", 1));
|
|
1299
1707
|
JS_SetPropertyStr(ctx, proto, "querySelector", JS_NewCFunction(ctx, js_el_querySelector, "querySelector", 1));
|
|
1300
1708
|
JS_SetPropertyStr(ctx, proto, "querySelectorAll", JS_NewCFunction(ctx, js_el_querySelectorAll, "querySelectorAll", 1));
|
|
1301
1709
|
JS_SetPropertyStr(ctx, proto, "getElementsByClassName", JS_NewCFunction(ctx, js_el_getElementsByClassName, "getElementsByClassName", 1));
|
|
1302
1710
|
JS_SetPropertyStr(ctx, proto, "getElementsByTagName", JS_NewCFunction(ctx, js_el_getElementsByTagName, "getElementsByTagName", 1));
|
|
1303
1711
|
JS_SetPropertyStr(ctx, proto, "closest", JS_NewCFunction(ctx, js_el_closest, "closest", 1));
|
|
1304
1712
|
JS_SetPropertyStr(ctx, proto, "insertAdjacentHTML", JS_NewCFunction(ctx, js_el_insertAdjacentHTML, "insertAdjacentHTML", 2));
|
|
1713
|
+
JS_SetPropertyStr(ctx, proto, "insertAdjacentElement", JS_NewCFunction(ctx, js_el_insertAdjacentElement, "insertAdjacentElement", 2));
|
|
1714
|
+
JS_SetPropertyStr(ctx, proto, "insertAdjacentText", JS_NewCFunction(ctx, js_el_insertAdjacentText, "insertAdjacentText", 2));
|
|
1305
1715
|
JS_SetPropertyStr(ctx, proto, "append", JS_NewCFunction(ctx, js_el_append, "append", 0));
|
|
1306
1716
|
JS_SetPropertyStr(ctx, proto, "prepend", JS_NewCFunction(ctx, js_el_prepend, "prepend", 0));
|
|
1307
1717
|
JS_SetPropertyStr(ctx, proto, "getBoundingClientRect", JS_NewCFunction(ctx, js_el_getBoundingClientRect, "getBoundingClientRect", 0));
|
|
1718
|
+
JS_SetPropertyStr(ctx, proto, "getClientRects", JS_NewCFunction(ctx, js_el_getClientRects, "getClientRects", 0));
|
|
1308
1719
|
|
|
1309
1720
|
define_prop(ctx, proto, "tagName", js_el_get_tagName, nullptr);
|
|
1721
|
+
define_prop(ctx, proto, "prefix", js_el_get_prefix, nullptr);
|
|
1310
1722
|
define_prop(ctx, proto, "id", js_el_get_id, js_el_set_id);
|
|
1311
1723
|
define_prop(ctx, proto, "className", js_el_get_className, js_el_set_className);
|
|
1312
1724
|
define_prop(ctx, proto, "value", js_el_get_value, js_el_set_value);
|
|
1313
1725
|
define_prop(ctx, proto, "checked", js_el_get_checked, js_el_set_checked);
|
|
1726
|
+
define_prop(ctx, proto, "disabled", js_el_get_disabled, js_el_set_disabled);
|
|
1727
|
+
define_prop(ctx, proto, "required", js_el_get_required, js_el_set_required);
|
|
1728
|
+
define_prop(ctx, proto, "readOnly", js_el_get_readOnly, js_el_set_readOnly);
|
|
1729
|
+
define_prop(ctx, proto, "multiple", js_el_get_multiple, js_el_set_multiple);
|
|
1730
|
+
define_prop(ctx, proto, "autofocus", js_el_get_autofocus, js_el_set_autofocus);
|
|
1731
|
+
define_prop(ctx, proto, "selected", js_el_get_selected, js_el_set_selected);
|
|
1732
|
+
define_prop(ctx, proto, "hidden", js_el_get_hidden, js_el_set_hidden);
|
|
1733
|
+
define_prop(ctx, proto, "title", js_el_get_title, js_el_set_title);
|
|
1734
|
+
define_prop(ctx, proto, "lang", js_el_get_lang, js_el_set_lang);
|
|
1735
|
+
define_prop(ctx, proto, "dir", js_el_get_dir, js_el_set_dir);
|
|
1314
1736
|
define_prop(ctx, proto, "innerHTML", js_el_get_innerHTML, js_el_set_innerHTML);
|
|
1315
1737
|
define_prop(ctx, proto, "outerHTML", js_el_get_outerHTML, nullptr);
|
|
1738
|
+
define_prop(ctx, proto, "innerText", js_el_get_textContent, js_el_set_textContent);
|
|
1316
1739
|
define_prop(ctx, proto, "children", js_el_get_children, nullptr);
|
|
1317
1740
|
define_prop(ctx, proto, "firstElementChild", js_el_get_firstElementChild, nullptr);
|
|
1318
1741
|
define_prop(ctx, proto, "lastElementChild", js_el_get_lastElementChild, nullptr);
|
|
@@ -1329,14 +1752,87 @@ void ElementBindings::install(JSContext* ctx) {
|
|
|
1329
1752
|
JSValue node_proto_ref = JS_GetClassProto(ctx, js_node_class_id);
|
|
1330
1753
|
JSValue element_proto_ref = JS_GetClassProto(ctx, js_element_class_id);
|
|
1331
1754
|
|
|
1332
|
-
|
|
1755
|
+
JSValue node_ctor = define_global_constructor(ctx, "Node", node_proto_ref);
|
|
1756
|
+
define_node_type_constants(ctx, node_ctor);
|
|
1757
|
+
JS_FreeValue(ctx, node_ctor);
|
|
1333
1758
|
JSValue element_ctor = define_global_constructor(ctx, "Element", element_proto_ref);
|
|
1334
1759
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
1335
1760
|
JS_SetPropertyStr(ctx, global, "HTMLElement", element_ctor);
|
|
1761
|
+
JS_SetPropertyStr(ctx, global, "__nativeCanonicalizeRootNode",
|
|
1762
|
+
JS_NewCFunction(ctx, js_canonicalize_root_node, "__nativeCanonicalizeRootNode", 1));
|
|
1336
1763
|
JS_FreeValue(ctx, global);
|
|
1337
1764
|
|
|
1338
1765
|
JS_FreeValue(ctx, node_proto_ref);
|
|
1339
1766
|
JS_FreeValue(ctx, element_proto_ref);
|
|
1767
|
+
|
|
1768
|
+
static const char* kGetRootNodeScript = R"JS(
|
|
1769
|
+
(function() {
|
|
1770
|
+
Node.prototype.getRootNode = function(options) {
|
|
1771
|
+
var composed = !!(options && options.composed);
|
|
1772
|
+
var node = this;
|
|
1773
|
+
while (true) {
|
|
1774
|
+
var parent = node.parentNode;
|
|
1775
|
+
if (parent) { node = parent; continue; }
|
|
1776
|
+
if (composed && node instanceof ShadowRoot) { node = node.host; continue; }
|
|
1777
|
+
return __nativeCanonicalizeRootNode(node);
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
Object.defineProperty(Node.prototype, 'isConnected', {
|
|
1782
|
+
get: function() { return this.getRootNode() === document; },
|
|
1783
|
+
enumerable: true,
|
|
1784
|
+
configurable: true,
|
|
1785
|
+
});
|
|
1786
|
+
|
|
1787
|
+
function locateNamespace(node, prefix) {
|
|
1788
|
+
if (!node) return null;
|
|
1789
|
+
if (node.nodeType === 1) {
|
|
1790
|
+
if (node.namespaceURI !== null && node.prefix === prefix) return node.namespaceURI;
|
|
1791
|
+
var attrs = node.attributes;
|
|
1792
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
1793
|
+
var a = attrs[i];
|
|
1794
|
+
if (prefix === null && a.name === 'xmlns') return a.value || null;
|
|
1795
|
+
if (prefix !== null && a.name === 'xmlns:' + prefix) return a.value || null;
|
|
1796
|
+
}
|
|
1797
|
+
return locateNamespace(node.parentElement, prefix);
|
|
1798
|
+
}
|
|
1799
|
+
if (node.nodeType === 9) {
|
|
1800
|
+
return node.documentElement ? locateNamespace(node.documentElement, prefix) : null;
|
|
1801
|
+
}
|
|
1802
|
+
if (node.nodeType === 10 || node.nodeType === 11) return null;
|
|
1803
|
+
return locateNamespace(node.parentElement, prefix);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
Node.prototype.lookupNamespaceURI = function(prefix) {
|
|
1807
|
+
if (prefix === undefined || prefix === '') prefix = null;
|
|
1808
|
+
return locateNamespace(this, prefix);
|
|
1809
|
+
};
|
|
1810
|
+
|
|
1811
|
+
Node.prototype.isDefaultNamespace = function(namespace) {
|
|
1812
|
+
if (namespace === undefined || namespace === '') namespace = null;
|
|
1813
|
+
return this.lookupNamespaceURI(null) === namespace;
|
|
1814
|
+
};
|
|
1815
|
+
|
|
1816
|
+
Node.prototype.lookupPrefix = function(namespace) {
|
|
1817
|
+
if (!namespace) return null;
|
|
1818
|
+
var node = this.nodeType === 9 ? this.documentElement : this;
|
|
1819
|
+
while (node && node.nodeType === 1) {
|
|
1820
|
+
if (node.namespaceURI === namespace && node.prefix !== null) return node.prefix;
|
|
1821
|
+
var attrs = node.attributes;
|
|
1822
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
1823
|
+
var a = attrs[i];
|
|
1824
|
+
if (a.name.indexOf('xmlns:') === 0 && a.value === namespace) return a.name.slice(6);
|
|
1825
|
+
}
|
|
1826
|
+
node = node.parentElement;
|
|
1827
|
+
}
|
|
1828
|
+
return null;
|
|
1829
|
+
};
|
|
1830
|
+
})();
|
|
1831
|
+
)JS";
|
|
1832
|
+
JSValue root_node_result = JS_Eval(ctx, kGetRootNodeScript, strlen(kGetRootNodeScript),
|
|
1833
|
+
"<get-root-node-bootstrap>", JS_EVAL_TYPE_GLOBAL);
|
|
1834
|
+
if (JS_IsException(root_node_result)) JS_FreeValue(ctx, JS_GetException(ctx));
|
|
1835
|
+
JS_FreeValue(ctx, root_node_result);
|
|
1340
1836
|
}
|
|
1341
1837
|
|
|
1342
1838
|
} // namespace margelo::nitro::nitrojsdom
|