@salve-software/react-native-nitro-jsdom 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NitroJsdom.podspec +63 -0
- package/README.md +95 -0
- package/android/CMakeLists.txt +86 -0
- package/android/build.gradle +148 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +9 -0
- package/android/src/main/java/com/nitrojsdom/NitroJsdomPackage.kt +20 -0
- package/cpp/HybridHtmlSandbox.cpp +171 -0
- package/cpp/HybridHtmlSandbox.hpp +47 -0
- package/cpp/lexbor/LexborDocument.cpp +365 -0
- package/cpp/lexbor/LexborDocument.hpp +71 -0
- package/cpp/quickjs/DOMBindings.cpp +57 -0
- package/cpp/quickjs/DOMBindings.hpp +21 -0
- package/cpp/quickjs/DOMBindingsInternal.cpp +157 -0
- package/cpp/quickjs/DOMBindingsInternal.hpp +83 -0
- package/cpp/quickjs/MutationObservers.cpp +546 -0
- package/cpp/quickjs/MutationObservers.hpp +127 -0
- package/cpp/quickjs/QuickJSRuntime.cpp +475 -0
- package/cpp/quickjs/QuickJSRuntime.hpp +110 -0
- package/cpp/quickjs/Storage.cpp +130 -0
- package/cpp/quickjs/Storage.hpp +40 -0
- package/cpp/quickjs/bindings/ClassListBindings.cpp +241 -0
- package/cpp/quickjs/bindings/ClassListBindings.hpp +29 -0
- package/cpp/quickjs/bindings/DatasetBindings.cpp +120 -0
- package/cpp/quickjs/bindings/DatasetBindings.hpp +20 -0
- package/cpp/quickjs/bindings/DocumentBindings.cpp +155 -0
- package/cpp/quickjs/bindings/DocumentBindings.hpp +19 -0
- package/cpp/quickjs/bindings/ElementBindings.cpp +1106 -0
- package/cpp/quickjs/bindings/ElementBindings.hpp +19 -0
- package/cpp/quickjs/bindings/EventBindings.cpp +352 -0
- package/cpp/quickjs/bindings/EventBindings.hpp +21 -0
- package/cpp/quickjs/bindings/FetchBindings.cpp +347 -0
- package/cpp/quickjs/bindings/FetchBindings.hpp +16 -0
- package/cpp/quickjs/bindings/StyleBindings.cpp +245 -0
- package/cpp/quickjs/bindings/StyleBindings.hpp +22 -0
- package/cpp/quickjs/bindings/TimerBindings.cpp +94 -0
- package/cpp/quickjs/bindings/TimerBindings.hpp +16 -0
- package/cpp/quickjs/bindings/WindowBindings.cpp +211 -0
- package/cpp/quickjs/bindings/WindowBindings.hpp +16 -0
- package/ios/Bridge.h +8 -0
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js +138 -0
- package/lib/commonjs/classes/JSDOM/JSDOM.class.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/index.js +17 -0
- package/lib/commonjs/classes/JSDOM/index.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/IFetchOptions.js +2 -0
- package/lib/commonjs/classes/JSDOM/types/IFetchOptions.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/IJSDOMOptions.js +6 -0
- package/lib/commonjs/classes/JSDOM/types/IJSDOMOptions.js.map +1 -0
- package/lib/commonjs/classes/JSDOM/types/index.js +2 -0
- package/lib/commonjs/classes/JSDOM/types/index.js.map +1 -0
- package/lib/commonjs/classes/index.js +17 -0
- package/lib/commonjs/classes/index.js.map +1 -0
- package/lib/commonjs/index.js +17 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/HtmlSandbox.nitro.js +6 -0
- package/lib/commonjs/specs/HtmlSandbox.nitro.js.map +1 -0
- package/lib/module/classes/JSDOM/JSDOM.class.js +133 -0
- package/lib/module/classes/JSDOM/JSDOM.class.js.map +1 -0
- package/lib/module/classes/JSDOM/index.js +4 -0
- package/lib/module/classes/JSDOM/index.js.map +1 -0
- package/lib/module/classes/JSDOM/types/IFetchOptions.js +2 -0
- package/lib/module/classes/JSDOM/types/IFetchOptions.js.map +1 -0
- package/lib/module/classes/JSDOM/types/IJSDOMOptions.js +4 -0
- package/lib/module/classes/JSDOM/types/IJSDOMOptions.js.map +1 -0
- package/lib/module/classes/JSDOM/types/index.js +2 -0
- package/lib/module/classes/JSDOM/types/index.js.map +1 -0
- package/lib/module/classes/index.js +4 -0
- package/lib/module/classes/index.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/HtmlSandbox.nitro.js +4 -0
- package/lib/module/specs/HtmlSandbox.nitro.js.map +1 -0
- package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts +74 -0
- package/lib/typescript/src/classes/JSDOM/JSDOM.class.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/index.d.ts +3 -0
- package/lib/typescript/src/classes/JSDOM/index.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/IFetchOptions.d.ts +14 -0
- package/lib/typescript/src/classes/JSDOM/types/IFetchOptions.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts +57 -0
- package/lib/typescript/src/classes/JSDOM/types/IJSDOMOptions.d.ts.map +1 -0
- package/lib/typescript/src/classes/JSDOM/types/index.d.ts +3 -0
- package/lib/typescript/src/classes/JSDOM/types/index.d.ts.map +1 -0
- package/lib/typescript/src/classes/index.d.ts +2 -0
- package/lib/typescript/src/classes/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts +13 -0
- package/lib/typescript/src/specs/HtmlSandbox.nitro.d.ts.map +1 -0
- package/nitro.json +26 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroJsdom+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroJsdom+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroJsdomOnLoad.cpp +49 -0
- package/nitrogen/generated/android/NitroJsdomOnLoad.hpp +34 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrojsdom/NitroJsdomOnLoad.kt +35 -0
- package/nitrogen/generated/ios/NitroJsdom+autolinking.rb +62 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Bridge.hpp +27 -0
- package/nitrogen/generated/ios/NitroJsdom-Swift-Cxx-Umbrella.hpp +38 -0
- package/nitrogen/generated/ios/NitroJsdomAutolinking.mm +35 -0
- package/nitrogen/generated/ios/NitroJsdomAutolinking.swift +16 -0
- package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.cpp +26 -0
- package/nitrogen/generated/shared/c++/HybridHtmlSandboxSpec.hpp +73 -0
- package/package.json +131 -0
- package/src/classes/JSDOM/JSDOM.class.ts +145 -0
- package/src/classes/JSDOM/index.ts +2 -0
- package/src/classes/JSDOM/types/IFetchOptions.ts +14 -0
- package/src/classes/JSDOM/types/IJSDOMOptions.ts +57 -0
- package/src/classes/JSDOM/types/index.ts +2 -0
- package/src/classes/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/specs/HtmlSandbox.nitro.ts +33 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <memory>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <optional>
|
|
6
|
+
#include <variant>
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
#include "HybridHtmlSandboxSpec.hpp"
|
|
11
|
+
#include "lexbor/LexborDocument.hpp"
|
|
12
|
+
#include "quickjs/QuickJSRuntime.hpp"
|
|
13
|
+
|
|
14
|
+
namespace margelo::nitro::nitrojsdom {
|
|
15
|
+
|
|
16
|
+
class HybridHtmlSandbox : public HybridHtmlSandboxSpec {
|
|
17
|
+
public:
|
|
18
|
+
HybridHtmlSandbox() : HybridObject(TAG), HybridHtmlSandboxSpec() {}
|
|
19
|
+
~HybridHtmlSandbox() override = default;
|
|
20
|
+
|
|
21
|
+
void initialize(const std::string& html, bool runScripts, const std::string& url) override;
|
|
22
|
+
|
|
23
|
+
std::shared_ptr<Promise<std::string>> evaluate(const std::string& script) override;
|
|
24
|
+
|
|
25
|
+
std::string serialize() override;
|
|
26
|
+
|
|
27
|
+
void setConsoleCallback(
|
|
28
|
+
const std::optional<std::variant<nitro::NullType, std::function<void(const std::string& level, const std::vector<std::string>& args)>>>& callback
|
|
29
|
+
) override;
|
|
30
|
+
|
|
31
|
+
void setDialogCallbacks(
|
|
32
|
+
const std::optional<std::variant<nitro::NullType, std::function<void(const std::string& /* message */)>>>& onAlert,
|
|
33
|
+
const std::optional<std::variant<nitro::NullType, std::function<std::shared_ptr<Promise<bool>>(const std::string& /* message */)>>>& onConfirm,
|
|
34
|
+
const std::optional<std::variant<nitro::NullType, std::function<std::shared_ptr<Promise<std::variant<nitro::NullType, std::string>>>(const std::string& /* message */, const std::optional<std::string>& /* defaultValue */)>>>& onPrompt
|
|
35
|
+
) override;
|
|
36
|
+
|
|
37
|
+
void setFetchCallback(
|
|
38
|
+
const std::optional<std::variant<nitro::NullType, std::function<std::shared_ptr<Promise<std::shared_ptr<Promise<std::string>>>>(const std::string& /* url */, const std::string& /* method */, const std::string& /* headersJson */, const std::optional<std::string>& /* body */)>>>& callback
|
|
39
|
+
) override;
|
|
40
|
+
|
|
41
|
+
private:
|
|
42
|
+
std::unique_ptr<LexborDocument> _document;
|
|
43
|
+
std::unique_ptr<QuickJSRuntime> _runtime;
|
|
44
|
+
bool _initialized { false };
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
#include "LexborDocument.hpp"
|
|
2
|
+
#include <lexbor/html/html.h>
|
|
3
|
+
#include <lexbor/css/css.h>
|
|
4
|
+
#include <lexbor/selectors/selectors.h>
|
|
5
|
+
#include <lexbor/dom/dom.h>
|
|
6
|
+
#include <stdexcept>
|
|
7
|
+
|
|
8
|
+
namespace margelo::nitro::nitrojsdom {
|
|
9
|
+
|
|
10
|
+
// ── Callbacks ───────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
static lxb_status_t serializeCallback(const lxb_char_t* data, size_t len, void* ctx) {
|
|
15
|
+
static_cast<std::string*>(ctx)->append(reinterpret_cast<const char*>(data), len);
|
|
16
|
+
return LXB_STATUS_OK;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static std::string serializeNode(lxb_dom_node_t* node) {
|
|
20
|
+
std::string result;
|
|
21
|
+
lxb_html_serialize_tree_cb(node, serializeCallback, &result);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
struct FindFirstCtx { lxb_dom_element_t* result = nullptr; };
|
|
26
|
+
|
|
27
|
+
static lxb_status_t findFirstCallback(lxb_dom_node_t* node, lxb_css_selector_specificity_t, void* ctx) {
|
|
28
|
+
static_cast<FindFirstCtx*>(ctx)->result = lxb_dom_interface_element(node);
|
|
29
|
+
return LXB_STATUS_STOP;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static lxb_status_t findAllStringCallback(lxb_dom_node_t* node, lxb_css_selector_specificity_t, void* ctx) {
|
|
33
|
+
static_cast<std::vector<std::string>*>(ctx)->push_back(serializeNode(node));
|
|
34
|
+
return LXB_STATUS_OK;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static lxb_status_t findAllPtrCallback(lxb_dom_node_t* node, lxb_css_selector_specificity_t, void* ctx) {
|
|
38
|
+
static_cast<std::vector<void*>*>(ctx)->push_back(lxb_dom_interface_element(node));
|
|
39
|
+
return LXB_STATUS_OK;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} // namespace
|
|
43
|
+
|
|
44
|
+
// ── Constructor / Destructor ─────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
LexborDocument::LexborDocument() {
|
|
47
|
+
auto* parser = lxb_css_parser_create();
|
|
48
|
+
lxb_css_parser_init(parser, nullptr);
|
|
49
|
+
_cssParser = parser;
|
|
50
|
+
|
|
51
|
+
auto* selectors = lxb_selectors_create();
|
|
52
|
+
lxb_selectors_init(selectors);
|
|
53
|
+
_selectors = selectors;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
LexborDocument::~LexborDocument() {
|
|
57
|
+
if (_selectors) lxb_selectors_destroy(static_cast<lxb_selectors_t*>(_selectors), true);
|
|
58
|
+
if (_cssParser) lxb_css_parser_destroy(static_cast<lxb_css_parser_t*>(_cssParser), true);
|
|
59
|
+
if (_document) {
|
|
60
|
+
lxb_html_document_destroy(static_cast<lxb_html_document_t*>(_document));
|
|
61
|
+
_document = nullptr;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Internal helpers ─────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
void* LexborDocument::findFirst(const std::string& selector) const {
|
|
68
|
+
if (!_document) return nullptr;
|
|
69
|
+
return findFirstFrom(lxb_dom_interface_node(static_cast<lxb_html_document_t*>(_document)), selector);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void* LexborDocument::findFirstFrom(void* startNode, const std::string& selector) const {
|
|
73
|
+
if (!startNode) return nullptr;
|
|
74
|
+
auto* parser = static_cast<lxb_css_parser_t*>(_cssParser);
|
|
75
|
+
auto* selectors = static_cast<lxb_selectors_t*>(_selectors);
|
|
76
|
+
|
|
77
|
+
lxb_css_selector_list_t* list = lxb_css_selectors_parse(parser,
|
|
78
|
+
reinterpret_cast<const lxb_char_t*>(selector.data()), selector.size());
|
|
79
|
+
if (!list) return nullptr;
|
|
80
|
+
|
|
81
|
+
FindFirstCtx ctx;
|
|
82
|
+
lxb_selectors_find(selectors, static_cast<lxb_dom_node_t*>(startNode), list, findFirstCallback, &ctx);
|
|
83
|
+
lxb_css_selector_list_destroy_memory(list);
|
|
84
|
+
return ctx.result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Parse / Serialize ────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
void LexborDocument::parse(const std::string& html) {
|
|
90
|
+
if (_document) lxb_html_document_destroy(static_cast<lxb_html_document_t*>(_document));
|
|
91
|
+
auto* doc = lxb_html_document_create();
|
|
92
|
+
if (!doc) throw std::runtime_error("Lexbor: failed to create document");
|
|
93
|
+
lxb_status_t status = lxb_html_document_parse(doc,
|
|
94
|
+
reinterpret_cast<const lxb_char_t*>(html.data()), html.size());
|
|
95
|
+
if (status != LXB_STATUS_OK) {
|
|
96
|
+
lxb_html_document_destroy(doc);
|
|
97
|
+
_document = nullptr;
|
|
98
|
+
throw std::runtime_error("Lexbor: failed to parse HTML");
|
|
99
|
+
}
|
|
100
|
+
_document = doc;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
std::string LexborDocument::serialize() const {
|
|
104
|
+
if (!_document) return "";
|
|
105
|
+
return serializeNode(lxb_dom_interface_node(static_cast<lxb_html_document_t*>(_document)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Element queries ──────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
void* LexborDocument::getElementById(const std::string& id) const {
|
|
111
|
+
// [id="..."] is safe for any id value, including those with special CSS chars
|
|
112
|
+
return findFirst("[id=\"" + id + "\"]");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
void* LexborDocument::querySelector_el(const std::string& sel) const {
|
|
116
|
+
return findFirst(sel);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
std::vector<void*> LexborDocument::querySelectorAll_el(const std::string& sel) const {
|
|
120
|
+
if (!_document) return {};
|
|
121
|
+
auto* parser = static_cast<lxb_css_parser_t*>(_cssParser);
|
|
122
|
+
auto* selectors = static_cast<lxb_selectors_t*>(_selectors);
|
|
123
|
+
auto* doc = static_cast<lxb_html_document_t*>(_document);
|
|
124
|
+
|
|
125
|
+
lxb_css_selector_list_t* list = lxb_css_selectors_parse(parser,
|
|
126
|
+
reinterpret_cast<const lxb_char_t*>(sel.data()), sel.size());
|
|
127
|
+
std::vector<void*> results;
|
|
128
|
+
if (list) {
|
|
129
|
+
lxb_selectors_find(selectors, lxb_dom_interface_node(doc), list, findAllPtrCallback, &results);
|
|
130
|
+
lxb_css_selector_list_destroy_memory(list);
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void* LexborDocument::querySelectorFromEl(void* element, const std::string& sel) const {
|
|
136
|
+
if (!element) return nullptr;
|
|
137
|
+
return findFirstFrom(lxb_dom_interface_node(static_cast<lxb_dom_element_t*>(element)), sel);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
std::vector<void*> LexborDocument::querySelectorAllFromEl(void* element, const std::string& sel) const {
|
|
141
|
+
if (!_document || !element) return {};
|
|
142
|
+
auto* parser = static_cast<lxb_css_parser_t*>(_cssParser);
|
|
143
|
+
auto* selectors = static_cast<lxb_selectors_t*>(_selectors);
|
|
144
|
+
auto* node = lxb_dom_interface_node(static_cast<lxb_dom_element_t*>(element));
|
|
145
|
+
|
|
146
|
+
lxb_css_selector_list_t* list = lxb_css_selectors_parse(parser,
|
|
147
|
+
reinterpret_cast<const lxb_char_t*>(sel.data()), sel.size());
|
|
148
|
+
std::vector<void*> results;
|
|
149
|
+
if (list) {
|
|
150
|
+
lxb_selectors_find(selectors, node, list, findAllPtrCallback, &results);
|
|
151
|
+
lxb_css_selector_list_destroy_memory(list);
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── Document structure ───────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
void* LexborDocument::body() const {
|
|
159
|
+
if (!_document) return nullptr;
|
|
160
|
+
return lxb_html_document_body_element(static_cast<lxb_html_document_t*>(_document));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
void* LexborDocument::head() const {
|
|
164
|
+
if (!_document) return nullptr;
|
|
165
|
+
return lxb_html_document_head_element(static_cast<lxb_html_document_t*>(_document));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
void* LexborDocument::documentElement() const {
|
|
169
|
+
if (!_document) return nullptr;
|
|
170
|
+
return lxb_dom_document_element(
|
|
171
|
+
lxb_dom_interface_document(static_cast<lxb_html_document_t*>(_document)));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Script extraction ────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
std::vector<std::string> LexborDocument::getScriptContents() const {
|
|
177
|
+
auto scriptEls = querySelectorAll_el("script");
|
|
178
|
+
std::vector<std::string> contents;
|
|
179
|
+
contents.reserve(scriptEls.size());
|
|
180
|
+
|
|
181
|
+
for (void* el : scriptEls) {
|
|
182
|
+
auto* node = lxb_dom_interface_node(static_cast<lxb_dom_element_t*>(el));
|
|
183
|
+
size_t len = 0;
|
|
184
|
+
lxb_char_t* text = lxb_dom_node_text_content(node, &len);
|
|
185
|
+
if (text && len > 0) {
|
|
186
|
+
contents.emplace_back(reinterpret_cast<char*>(text), len);
|
|
187
|
+
lxb_dom_document_destroy_text(node->owner_document, text);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return contents;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Node creation ─────────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
void* LexborDocument::createElement(const std::string& tag) {
|
|
196
|
+
if (!_document) return nullptr;
|
|
197
|
+
auto* dom_doc = lxb_dom_interface_document(static_cast<lxb_html_document_t*>(_document));
|
|
198
|
+
return lxb_dom_document_create_element(dom_doc,
|
|
199
|
+
reinterpret_cast<const lxb_char_t*>(tag.data()), tag.size(), nullptr);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
void* LexborDocument::createTextNode(const std::string& text) {
|
|
203
|
+
if (!_document) return nullptr;
|
|
204
|
+
auto* dom_doc = lxb_dom_interface_document(static_cast<lxb_html_document_t*>(_document));
|
|
205
|
+
return lxb_dom_document_create_text_node(dom_doc,
|
|
206
|
+
reinterpret_cast<const lxb_char_t*>(text.data()), text.size());
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
void* LexborDocument::createComment(const std::string& text) {
|
|
210
|
+
if (!_document) return nullptr;
|
|
211
|
+
auto* dom_doc = lxb_dom_interface_document(static_cast<lxb_html_document_t*>(_document));
|
|
212
|
+
return lxb_dom_document_create_comment(dom_doc,
|
|
213
|
+
reinterpret_cast<const lxb_char_t*>(text.data()), text.size());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void* LexborDocument::createDocumentFragment() {
|
|
217
|
+
if (!_document) return nullptr;
|
|
218
|
+
auto* dom_doc = lxb_dom_interface_document(static_cast<lxb_html_document_t*>(_document));
|
|
219
|
+
return lxb_dom_document_create_document_fragment(dom_doc);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ── Element content ──────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
void LexborDocument::setTextContentOnEl(void* element, const std::string& text) {
|
|
225
|
+
auto* el = static_cast<lxb_dom_element_t*>(element);
|
|
226
|
+
auto* node = lxb_dom_interface_node(el);
|
|
227
|
+
|
|
228
|
+
// Remove all existing children
|
|
229
|
+
while (node->first_child) {
|
|
230
|
+
lxb_dom_node_t* child = node->first_child;
|
|
231
|
+
lxb_dom_node_remove(child);
|
|
232
|
+
lxb_dom_node_destroy_deep(child);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (text.empty()) return;
|
|
236
|
+
|
|
237
|
+
auto* dom_doc = node->owner_document;
|
|
238
|
+
auto* text_node = lxb_dom_document_create_text_node(dom_doc,
|
|
239
|
+
reinterpret_cast<const lxb_char_t*>(text.data()), text.size());
|
|
240
|
+
if (text_node) {
|
|
241
|
+
lxb_dom_node_insert_child(node, lxb_dom_interface_node(text_node));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
void LexborDocument::setInnerHTMLOnEl(void* element, const std::string& html) {
|
|
246
|
+
auto* el = static_cast<lxb_dom_element_t*>(element);
|
|
247
|
+
auto* doc = static_cast<lxb_html_document_t*>(_document);
|
|
248
|
+
auto* root = lxb_dom_interface_node(el);
|
|
249
|
+
|
|
250
|
+
lxb_dom_node_t* frag = lxb_html_document_parse_fragment(doc, el,
|
|
251
|
+
reinterpret_cast<const lxb_char_t*>(html.data()), html.size());
|
|
252
|
+
if (!frag) return;
|
|
253
|
+
|
|
254
|
+
while (root->first_child) lxb_dom_node_destroy_deep(root->first_child);
|
|
255
|
+
|
|
256
|
+
while (frag->first_child) {
|
|
257
|
+
lxb_dom_node_t* child = frag->first_child;
|
|
258
|
+
lxb_dom_node_remove(child);
|
|
259
|
+
lxb_dom_node_insert_child(root, child);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
lxb_dom_node_destroy(frag);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
std::vector<void*> LexborDocument::parseFragmentNodes(void* contextElement, const std::string& html) {
|
|
266
|
+
auto* el = static_cast<lxb_dom_element_t*>(contextElement);
|
|
267
|
+
auto* doc = static_cast<lxb_html_document_t*>(_document);
|
|
268
|
+
|
|
269
|
+
lxb_dom_node_t* frag = lxb_html_document_parse_fragment(doc, el,
|
|
270
|
+
reinterpret_cast<const lxb_char_t*>(html.data()), html.size());
|
|
271
|
+
if (!frag) return {};
|
|
272
|
+
|
|
273
|
+
std::vector<void*> nodes;
|
|
274
|
+
while (frag->first_child) {
|
|
275
|
+
lxb_dom_node_t* child = frag->first_child;
|
|
276
|
+
lxb_dom_node_remove(child);
|
|
277
|
+
nodes.push_back(child);
|
|
278
|
+
}
|
|
279
|
+
lxb_dom_node_destroy(frag);
|
|
280
|
+
return nodes;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ── Selector matching ────────────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
bool LexborDocument::matchesSelector(void* element, const std::string& sel) const {
|
|
286
|
+
auto results = querySelectorAll_el(sel);
|
|
287
|
+
for (void* el : results) {
|
|
288
|
+
if (el == element) return true;
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Legacy selector-based API ────────────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
std::optional<std::string> LexborDocument::querySelector(const std::string& selector) const {
|
|
296
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
297
|
+
if (!el) return std::nullopt;
|
|
298
|
+
return serializeNode(lxb_dom_interface_node(el));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
std::vector<std::string> LexborDocument::querySelectorAll(const std::string& selector) const {
|
|
302
|
+
if (!_document) return {};
|
|
303
|
+
auto* parser = static_cast<lxb_css_parser_t*>(_cssParser);
|
|
304
|
+
auto* selectors = static_cast<lxb_selectors_t*>(_selectors);
|
|
305
|
+
auto* doc = static_cast<lxb_html_document_t*>(_document);
|
|
306
|
+
|
|
307
|
+
lxb_css_selector_list_t* list = lxb_css_selectors_parse(parser,
|
|
308
|
+
reinterpret_cast<const lxb_char_t*>(selector.data()), selector.size());
|
|
309
|
+
std::vector<std::string> results;
|
|
310
|
+
if (list) {
|
|
311
|
+
lxb_selectors_find(selectors, lxb_dom_interface_node(doc), list, findAllStringCallback, &results);
|
|
312
|
+
lxb_css_selector_list_destroy_memory(list);
|
|
313
|
+
}
|
|
314
|
+
return results;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
std::optional<std::string> LexborDocument::getAttribute(const std::string& selector, const std::string& attr) const {
|
|
318
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
319
|
+
if (!el) return std::nullopt;
|
|
320
|
+
size_t val_len;
|
|
321
|
+
const lxb_char_t* val = lxb_dom_element_get_attribute(el,
|
|
322
|
+
reinterpret_cast<const lxb_char_t*>(attr.data()), attr.size(), &val_len);
|
|
323
|
+
if (!val) return std::nullopt;
|
|
324
|
+
return std::string(reinterpret_cast<const char*>(val), val_len);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
void LexborDocument::setAttribute(const std::string& selector, const std::string& attr, const std::string& value) {
|
|
328
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
329
|
+
if (!el) return;
|
|
330
|
+
lxb_dom_element_set_attribute(el,
|
|
331
|
+
reinterpret_cast<const lxb_char_t*>(attr.data()), attr.size(),
|
|
332
|
+
reinterpret_cast<const lxb_char_t*>(value.data()), value.size());
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
std::optional<std::string> LexborDocument::getTextContent(const std::string& selector) const {
|
|
336
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
337
|
+
if (!el) return std::nullopt;
|
|
338
|
+
lxb_dom_node_t* node = lxb_dom_interface_node(el);
|
|
339
|
+
size_t len;
|
|
340
|
+
lxb_char_t* text = lxb_dom_node_text_content(node, &len);
|
|
341
|
+
if (!text) return std::nullopt;
|
|
342
|
+
std::string result(reinterpret_cast<char*>(text), len);
|
|
343
|
+
lxb_dom_document_destroy_text(node->owner_document, text);
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
std::optional<std::string> LexborDocument::getInnerHTML(const std::string& selector) const {
|
|
348
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
349
|
+
if (!el) return std::nullopt;
|
|
350
|
+
std::string result;
|
|
351
|
+
lxb_dom_node_t* child = lxb_dom_interface_node(el)->first_child;
|
|
352
|
+
while (child) {
|
|
353
|
+
result += serializeNode(child);
|
|
354
|
+
child = child->next;
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
void LexborDocument::setInnerHTML(const std::string& selector, const std::string& html) {
|
|
360
|
+
auto* el = static_cast<lxb_dom_element_t*>(findFirst(selector));
|
|
361
|
+
if (!el) return;
|
|
362
|
+
setInnerHTMLOnEl(el, html);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <optional>
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
namespace margelo::nitro::nitrojsdom {
|
|
8
|
+
|
|
9
|
+
class LexborDocument {
|
|
10
|
+
public:
|
|
11
|
+
LexborDocument();
|
|
12
|
+
~LexborDocument();
|
|
13
|
+
|
|
14
|
+
// ── Parse / Serialize ──────────────────────────────────────────────────────
|
|
15
|
+
void parse(const std::string& html);
|
|
16
|
+
std::string serialize() const;
|
|
17
|
+
|
|
18
|
+
// ── Raw document pointer (void* = lxb_html_document_t*) ──────────────────
|
|
19
|
+
void* documentHtmlPtr() const { return _document; }
|
|
20
|
+
|
|
21
|
+
// ── Element queries — return void* (lxb_dom_element_t*) ──────────────────
|
|
22
|
+
void* getElementById(const std::string& id) const;
|
|
23
|
+
void* querySelector_el(const std::string& sel) const;
|
|
24
|
+
std::vector<void*> querySelectorAll_el(const std::string& sel) const;
|
|
25
|
+
void* querySelectorFromEl(void* element, const std::string& sel) const;
|
|
26
|
+
std::vector<void*> querySelectorAllFromEl(void* element, const std::string& sel) const;
|
|
27
|
+
|
|
28
|
+
// ── Document structure ────────────────────────────────────────────────────
|
|
29
|
+
void* body() const;
|
|
30
|
+
void* head() const;
|
|
31
|
+
void* documentElement() const;
|
|
32
|
+
|
|
33
|
+
// ── Script extraction ────────────────────────────────────────────────────
|
|
34
|
+
std::vector<std::string> getScriptContents() const;
|
|
35
|
+
|
|
36
|
+
// ── Node creation ─────────────────────────────────────────────────────────
|
|
37
|
+
void* createElement(const std::string& tag);
|
|
38
|
+
void* createTextNode(const std::string& text);
|
|
39
|
+
void* createComment(const std::string& text);
|
|
40
|
+
void* createDocumentFragment();
|
|
41
|
+
|
|
42
|
+
// ── Element content ───────────────────────────────────────────────────────
|
|
43
|
+
void setTextContentOnEl(void* element, const std::string& text);
|
|
44
|
+
void setInnerHTMLOnEl(void* element, const std::string& html);
|
|
45
|
+
|
|
46
|
+
// Like setInnerHTMLOnEl, but returns the detached parsed nodes instead of
|
|
47
|
+
// replacing contextElement's children — used by insertAdjacentHTML.
|
|
48
|
+
std::vector<void*> parseFragmentNodes(void* contextElement, const std::string& html);
|
|
49
|
+
|
|
50
|
+
// ── Selector matching ─────────────────────────────────────────────────────
|
|
51
|
+
bool matchesSelector(void* element, const std::string& sel) const;
|
|
52
|
+
|
|
53
|
+
// ── Legacy selector-based API (kept for backward compat) ─────────────────
|
|
54
|
+
std::optional<std::string> querySelector(const std::string& selector) const;
|
|
55
|
+
std::vector<std::string> querySelectorAll(const std::string& selector) const;
|
|
56
|
+
std::optional<std::string> getAttribute(const std::string& selector, const std::string& attr) const;
|
|
57
|
+
void setAttribute(const std::string& selector, const std::string& attr, const std::string& value);
|
|
58
|
+
std::optional<std::string> getTextContent(const std::string& selector) const;
|
|
59
|
+
std::optional<std::string> getInnerHTML(const std::string& selector) const;
|
|
60
|
+
void setInnerHTML(const std::string& selector, const std::string& html);
|
|
61
|
+
|
|
62
|
+
private:
|
|
63
|
+
void* _document { nullptr };
|
|
64
|
+
void* _cssParser { nullptr };
|
|
65
|
+
void* _selectors { nullptr };
|
|
66
|
+
|
|
67
|
+
void* findFirst(const std::string& selector) const;
|
|
68
|
+
void* findFirstFrom(void* startNode, const std::string& selector) const;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#include "DOMBindings.hpp"
|
|
2
|
+
#include "DOMBindingsInternal.hpp"
|
|
3
|
+
#include "MutationObservers.hpp"
|
|
4
|
+
#include "Storage.hpp"
|
|
5
|
+
#include "QuickJSRuntime.hpp"
|
|
6
|
+
#include "../lexbor/LexborDocument.hpp"
|
|
7
|
+
#include "bindings/ElementBindings.hpp"
|
|
8
|
+
#include "bindings/DocumentBindings.hpp"
|
|
9
|
+
#include "bindings/EventBindings.hpp"
|
|
10
|
+
#include "bindings/TimerBindings.hpp"
|
|
11
|
+
#include "bindings/WindowBindings.hpp"
|
|
12
|
+
#include "bindings/FetchBindings.hpp"
|
|
13
|
+
#include <lexbor/html/html.h>
|
|
14
|
+
#include <lexbor/dom/dom.h>
|
|
15
|
+
|
|
16
|
+
// DOMBindings::install() is the single orchestrator for every QuickJS binding
|
|
17
|
+
// module below. Each module owns one concern and exposes `install(JSContext*)`;
|
|
18
|
+
// this function just sequences them (some depend on an earlier one having
|
|
19
|
+
// already registered a class or global — see the comments in each header).
|
|
20
|
+
|
|
21
|
+
namespace margelo::nitro::nitrojsdom {
|
|
22
|
+
|
|
23
|
+
void DOMBindings::install(QuickJSRuntime* runtime, LexborDocument* document) {
|
|
24
|
+
JSContext* ctx = static_cast<JSContext*>(runtime->context());
|
|
25
|
+
(void)document;
|
|
26
|
+
|
|
27
|
+
ElementBindings::install(ctx); // registers the Element class + proto
|
|
28
|
+
DocumentBindings::install(ctx); // creates globalThis.document
|
|
29
|
+
EventBindings::install(ctx); // needs Element's proto + globalThis.document to exist
|
|
30
|
+
TimerBindings::install(ctx);
|
|
31
|
+
WindowBindings::install(ctx);
|
|
32
|
+
FetchBindings::install(ctx); // XHR bootstrap uses `new Event(...)`, so must run after EventBindings
|
|
33
|
+
|
|
34
|
+
// ── localStorage / sessionStorage ──────────────────────────────────────────
|
|
35
|
+
{
|
|
36
|
+
RuntimeContext* rctx = get_ctx(ctx);
|
|
37
|
+
if (rctx) {
|
|
38
|
+
installStorage(ctx, "localStorage", &rctx->local_storage);
|
|
39
|
+
installStorage(ctx, "sessionStorage", &rctx->session_storage);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ── MutationObserver ───────────────────────────────────────────────────────
|
|
44
|
+
{
|
|
45
|
+
RuntimeContext* rctx = get_ctx(ctx);
|
|
46
|
+
if (rctx && rctx->mutation_observers && rctx->document) {
|
|
47
|
+
// Pass the lxb_dom_document_t* root node as the doc_root for ancestry checks
|
|
48
|
+
void* html_doc = rctx->document->documentHtmlPtr();
|
|
49
|
+
void* doc_node = lxb_dom_interface_node(
|
|
50
|
+
lxb_dom_interface_document(
|
|
51
|
+
static_cast<lxb_html_document_t*>(html_doc)));
|
|
52
|
+
rctx->mutation_observers->install(ctx, doc_node);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
} // namespace margelo::nitro::nitrojsdom
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
namespace margelo::nitro::nitrojsdom {
|
|
4
|
+
|
|
5
|
+
class LexborDocument;
|
|
6
|
+
class QuickJSRuntime;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Registers document.*, window.*, and console.* bindings inside a QuickJS context,
|
|
10
|
+
* delegating DOM operations to the provided LexborDocument.
|
|
11
|
+
*
|
|
12
|
+
* Called once during QuickJSRuntime::bindDocument().
|
|
13
|
+
*/
|
|
14
|
+
class DOMBindings {
|
|
15
|
+
public:
|
|
16
|
+
// Register all built-in JS ↔ Lexbor bindings on the given runtime/context pair.
|
|
17
|
+
// `runtime` and `document` must outlive this call.
|
|
18
|
+
static void install(QuickJSRuntime* runtime, LexborDocument* document);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
} // namespace margelo::nitro::nitrojsdom
|