wasm improvements
This commit is contained in:
parent
42a479572d
commit
6aa0338c0f
9 changed files with 603 additions and 70 deletions
|
|
@ -20,8 +20,20 @@ namespace Crafter::DomBindings {
|
|||
void AddPopStateListener(std::int32_t id);
|
||||
__attribute__((import_module("env"), import_name("removePopStateListener")))
|
||||
void RemovePopStateListener(std::int32_t id);
|
||||
__attribute__((import_module("env"), import_name("replaceState")))
|
||||
void ReplaceState(const char* data, std::int32_t dataLength,
|
||||
const char* title, std::int32_t titleLength,
|
||||
const char* url, std::int32_t urlLength);
|
||||
__attribute__((import_module("env"), import_name("getPathName")))
|
||||
const char* GetPathName();
|
||||
__attribute__((import_module("env"), import_name("getSearch")))
|
||||
const char* GetSearch();
|
||||
__attribute__((import_module("env"), import_name("getHash")))
|
||||
const char* GetHash();
|
||||
__attribute__((import_module("env"), import_name("getHref")))
|
||||
const char* GetHref();
|
||||
__attribute__((import_module("env"), import_name("navigate")))
|
||||
void Navigate(const char* url, std::int32_t urlLength, bool replace);
|
||||
|
||||
// Defined in Crafter.Graphics-Dom.cpp.
|
||||
std::int32_t PopStateRegister(std::function<void()> cb);
|
||||
|
|
@ -48,11 +60,114 @@ namespace Crafter::Router {
|
|||
Crafter::DomBindings::PopStateUnregister(id);
|
||||
}
|
||||
|
||||
void ReplaceState(std::string_view data, std::string_view title, std::string_view url) {
|
||||
Crafter::DomBindings::ReplaceState(
|
||||
data.data(), static_cast<std::int32_t>(data.size()),
|
||||
title.data(), static_cast<std::int32_t>(title.size()),
|
||||
url.data(), static_cast<std::int32_t>(url.size()));
|
||||
}
|
||||
|
||||
// The four location accessors share the same shape: the JS side
|
||||
// WasmAllocs a NUL-terminated UTF-8 copy, we adopt it into a
|
||||
// std::string and free the raw buffer. A null return means the JS
|
||||
// allocation failed.
|
||||
namespace {
|
||||
std::string AdoptJsString(const char* raw) {
|
||||
if (!raw) return {};
|
||||
std::string out(raw);
|
||||
std::free(const_cast<char*>(raw));
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPath() {
|
||||
const char* raw = Crafter::DomBindings::GetPathName();
|
||||
if (!raw) return {};
|
||||
std::string out(raw);
|
||||
std::free(const_cast<char*>(raw));
|
||||
return AdoptJsString(Crafter::DomBindings::GetPathName());
|
||||
}
|
||||
|
||||
std::string GetSearch() {
|
||||
return AdoptJsString(Crafter::DomBindings::GetSearch());
|
||||
}
|
||||
|
||||
std::string GetHash() {
|
||||
return AdoptJsString(Crafter::DomBindings::GetHash());
|
||||
}
|
||||
|
||||
std::string GetHref() {
|
||||
return AdoptJsString(Crafter::DomBindings::GetHref());
|
||||
}
|
||||
|
||||
void Navigate(std::string_view url, bool replace) {
|
||||
Crafter::DomBindings::Navigate(
|
||||
url.data(), static_cast<std::int32_t>(url.size()), replace);
|
||||
}
|
||||
|
||||
// ─── Query-string parsing ─────────────────────────────────────────
|
||||
//
|
||||
// Pure string work, no JS round-trip. Kept here rather than left to
|
||||
// callers because every consumer would otherwise hand-roll percent
|
||||
// decoding, and getting `+` vs `%20` wrong is the usual bug.
|
||||
|
||||
namespace {
|
||||
int HexVal(char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string PercentDecode(std::string_view in) {
|
||||
std::string out;
|
||||
out.reserve(in.size());
|
||||
for (std::size_t i = 0; i < in.size(); ++i) {
|
||||
const char c = in[i];
|
||||
if (c == '+') {
|
||||
// application/x-www-form-urlencoded encodes space as '+'.
|
||||
out.push_back(' ');
|
||||
} else if (c == '%' && i + 2 < in.size()) {
|
||||
const int hi = HexVal(in[i + 1]);
|
||||
const int lo = HexVal(in[i + 2]);
|
||||
if (hi >= 0 && lo >= 0) {
|
||||
out.push_back(static_cast<char>(hi * 16 + lo));
|
||||
i += 2;
|
||||
} else {
|
||||
// Malformed escape: pass the '%' through rather than
|
||||
// dropping input. Lets a stray '%' in a search box
|
||||
// survive a round trip.
|
||||
out.push_back(c);
|
||||
}
|
||||
} else {
|
||||
out.push_back(c);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> ParseQuery(std::string_view query) {
|
||||
std::vector<std::pair<std::string, std::string>> out;
|
||||
if (!query.empty() && query.front() == '?') query.remove_prefix(1);
|
||||
while (!query.empty()) {
|
||||
const std::size_t amp = query.find('&');
|
||||
std::string_view pair = query.substr(0, amp);
|
||||
query = (amp == std::string_view::npos) ? std::string_view{}
|
||||
: query.substr(amp + 1);
|
||||
if (pair.empty()) continue; // tolerate "a=1&&b=2" and a trailing '&'
|
||||
const std::size_t eq = pair.find('=');
|
||||
if (eq == std::string_view::npos) {
|
||||
// Valueless key ("?debug") — present, empty value.
|
||||
out.emplace_back(PercentDecode(pair), std::string{});
|
||||
} else {
|
||||
out.emplace_back(PercentDecode(pair.substr(0, eq)),
|
||||
PercentDecode(pair.substr(eq + 1)));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<std::string> QueryGet(std::string_view query, std::string_view key) {
|
||||
for (auto& [k, v] : ParseQuery(query)) {
|
||||
if (k == key) return v;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue