Skip to content

Commit 1d94930

Browse files
committed
vaev-loader: Oops forgor to push that.
1 parent a19f8ca commit 1d94930

File tree

3 files changed

+170
-0
lines changed

3 files changed

+170
-0
lines changed

src/web/vaev-loader/loader.cpp

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
module;
2+
3+
#include <karm-gc/heap.h>
4+
#include <karm-mime/mime.h>
5+
#include <karm-mime/url.h>
6+
#include <karm-sys/file.h>
7+
#include <vaev-dom/document.h>
8+
#include <vaev-dom/html/parser.h>
9+
#include <vaev-dom/xml/parser.h>
10+
#include <vaev-style/stylesheet.h>
11+
12+
export module Vaev.Loader:loader;
13+
14+
import Karm.Http;
15+
import Karm.Aio;
16+
17+
namespace Vaev::Loader {
18+
19+
Async::Task<Gc::Ref<Dom::Document>> _loadDocumentAsync(Gc::Heap& heap, Mime::Url url, Rc<Http::Response> resp) {
20+
auto dom = heap.alloc<Dom::Document>(url);
21+
22+
auto mime = resp->header.contentType();
23+
24+
if (not mime.has())
25+
mime = Mime::sniffSuffix(url.path.suffix());
26+
27+
if (not resp->body)
28+
co_return Error::invalidInput("response body is missing");
29+
30+
if (not mime.has() and url == "fd:stdin"_url) {
31+
mime = "text/html"_mime;
32+
logInfo("assuming stdin is {}", mime);
33+
}
34+
35+
if (not mime.has())
36+
co_return Error::invalidInput("cannot determine MIME type");
37+
38+
auto respBody = resp->body.unwrap();
39+
auto buf = co_trya$(Aio::readAllUtf8Async(*respBody));
40+
41+
if (mime->is("text/html"_mime)) {
42+
Dom::HtmlParser parser{heap, dom};
43+
parser.write(buf);
44+
45+
co_return Ok(dom);
46+
} else if (mime->is("application/xhtml+xml"_mime)) {
47+
Io::SScan scan{buf};
48+
Dom::XmlParser parser{heap};
49+
co_try$(parser.parse(scan, HTML, *dom));
50+
51+
co_return Ok(dom);
52+
} else if (mime->is("text/plain"_mime)) {
53+
auto text = heap.alloc<Dom::Text>();
54+
text->appendData(buf);
55+
dom->appendChild(text);
56+
co_return Ok(dom);
57+
} else if (mime->is("image/svg+xml"_mime)) {
58+
Io::SScan scan{buf};
59+
Dom::XmlParser parser{heap};
60+
co_try$(parser.parse(scan, SVG, *dom));
61+
62+
co_return Ok(dom);
63+
} else {
64+
logError("unsupported MIME type: {}", mime);
65+
66+
co_return Error::invalidInput("unsupported MIME type");
67+
}
68+
}
69+
70+
export Async::Task<Gc::Ref<Dom::Document>> viewSourceAsync(Gc::Heap& heap, Http::Client& client, Mime::Url const& url) {
71+
auto resp = co_trya$(client.getAsync(url));
72+
if (not resp->body)
73+
co_return Error::invalidInput("response body is missing");
74+
auto respBody = resp->body.unwrap();
75+
auto buf = co_trya$(Aio::readAllUtf8Async(*respBody));
76+
77+
auto dom = heap.alloc<Dom::Document>(url);
78+
auto body = heap.alloc<Dom::Element>(Html::BODY);
79+
dom->appendChild(body);
80+
auto pre = heap.alloc<Dom::Element>(Html::PRE);
81+
body->appendChild(pre);
82+
auto text = heap.alloc<Dom::Text>(buf);
83+
pre->appendChild(text);
84+
85+
co_return Ok(dom);
86+
}
87+
88+
Async::Task<Style::StyleSheet> _fetchStylesheetAsync(Http::Client& client, Mime::Url url, Style::Origin origin) {
89+
auto resp = co_trya$(client.getAsync(url));
90+
91+
auto respBody = resp->body.unwrap();
92+
auto buf = co_trya$(Aio::readAllUtf8Async(*respBody));
93+
94+
Io::SScan s{buf};
95+
co_return Ok(Style::StyleSheet::parse(s, url, origin));
96+
}
97+
98+
Async::Task<> _fetchStylesheetsAsync(Http::Client& client, Gc::Ref<Dom::Node> node, Style::StyleSheetList& sb) {
99+
auto el = node->is<Dom::Element>();
100+
if (el and el->tagName == Html::STYLE) {
101+
auto text = el->textContent();
102+
Io::SScan textScan{text};
103+
auto sheet = Style::StyleSheet::parse(textScan, node->baseURI());
104+
sb.add(std::move(sheet));
105+
} else if (el and el->tagName == Html::LINK) {
106+
auto rel = el->getAttribute(Html::REL_ATTR);
107+
if (rel == "stylesheet"s) {
108+
auto href = el->getAttribute(Html::HREF_ATTR);
109+
if (not href) {
110+
logWarn("link element missing href attribute");
111+
co_return Error::invalidInput("link element missing href");
112+
}
113+
114+
auto url = Mime::Url::resolveReference(node->baseURI(), Mime::parseUrlOrPath(*href));
115+
if (not url) {
116+
logWarn("failed to resolve stylesheet url: {}", url);
117+
co_return Error::invalidInput("failed to resolve stylesheet url");
118+
}
119+
120+
auto sheet = co_await _fetchStylesheetAsync(client, url.unwrap(), Style::Origin::AUTHOR);
121+
if (not sheet) {
122+
logWarn("failed to fetch stylesheet from {}: {}", url, sheet);
123+
co_return Error::invalidInput("failed to fetch stylesheet from {}");
124+
}
125+
126+
sb.add(sheet.take());
127+
}
128+
} else {
129+
for (auto child = node->firstChild(); child; child = child->nextSibling())
130+
(void)co_await _fetchStylesheetsAsync(client, *child, sb);
131+
}
132+
133+
co_return Ok();
134+
}
135+
136+
export Async::Task<Gc::Ref<Dom::Document>> fetchDocumentAsync(Gc::Heap& heap, Http::Client& client, Mime::Url const& url) {
137+
if (url.scheme == "about") {
138+
if (url.path.str() == "blank")
139+
co_return co_await fetchDocumentAsync(heap, client, "bundle://vaev-driver/blank.xhtml"_url);
140+
141+
if (url.path.str() == "start")
142+
co_return co_await fetchDocumentAsync(heap, client, "bundle://vaev-driver/start-page.xhtml"_url);
143+
}
144+
145+
auto resp = co_trya$(client.getAsync(url));
146+
auto dom = co_trya$(_loadDocumentAsync(heap, url, resp));
147+
auto stylesheets = heap.alloc<Style::StyleSheetList>();
148+
stylesheets->add((co_await _fetchStylesheetAsync(client, "bundle://vaev-driver/html.css"_url, Style::Origin::USER_AGENT))
149+
.take("user agent stylesheet not available"));
150+
(void)co_await _fetchStylesheetsAsync(client, *dom, *stylesheets);
151+
dom->styleSheets = stylesheets;
152+
co_return Ok(dom);
153+
}
154+
155+
} // namespace Vaev::Loader

src/web/vaev-loader/manifest.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
3+
"id": "vaev-loader",
4+
"type": "lib",
5+
"description": "HTTP Document loader",
6+
"requires": [
7+
"vaev-dom",
8+
"vaev-style",
9+
"karm-http",
10+
"karm-gc"
11+
]
12+
}

src/web/vaev-loader/mod.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export module Vaev.Loader;
2+
3+
export import :loader;

0 commit comments

Comments
 (0)