From e8e4a06734bcf118abdf121367fe858ba8374a08 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Sat, 1 Aug 2026 08:52:38 -0400 Subject: [PATCH] v0.0.67 - Fix local:// scheme: strip query params from file paths, resolve root-relative paths against page directory, use text/javascript MIME for .mjs modules --- VERSION | 2 +- src/local_scheme.c | 44 +++++++++++- src/version.h | 4 +- .../local-site/shared-worker-import-test.html | 53 ++++++++++++++ tests/local-site/shared-worker-import-test.js | 31 ++++++++ tests/local-site/shared-worker-test.html | 71 +++++++++++++++++++ tests/local-site/shared-worker-test.js | 29 ++++++++ 7 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 tests/local-site/shared-worker-import-test.html create mode 100644 tests/local-site/shared-worker-import-test.js create mode 100644 tests/local-site/shared-worker-test.html create mode 100644 tests/local-site/shared-worker-test.js diff --git a/VERSION b/VERSION index ff8026f..9c3f756 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.66 +0.0.67 diff --git a/src/local_scheme.c b/src/local_scheme.c index 7e0c32a..e682e4b 100644 --- a/src/local_scheme.c +++ b/src/local_scheme.c @@ -35,7 +35,7 @@ static const char *guess_mime(const char *path) { if (g_ascii_strcasecmp(ext, ".js") == 0) return "application/javascript; charset=utf-8"; if (g_ascii_strcasecmp(ext, ".mjs") == 0) - return "application/javascript; charset=utf-8"; + return "text/javascript; charset=utf-8"; if (g_ascii_strcasecmp(ext, ".json") == 0) return "application/json"; if (g_ascii_strcasecmp(ext, ".svg") == 0) @@ -88,14 +88,50 @@ static void on_local_scheme(WebKitURISchemeRequest *request, gpointer user_data) /* Strip "local://" prefix to get the file path. The URI is * local:///absolute/path, so we skip 8 characters ("local://") * and the result is /absolute/path. */ - const char *path = uri + 8; - if (path == NULL || path[0] == '\0') { + const char *path_start = uri + 8; + if (path_start == NULL || path_start[0] == '\0') { g_printerr("[local-scheme] Empty path in URI: %s\n", uri); webkit_uri_scheme_request_finish_error(request, g_error_new_literal( g_quark_from_static_string("local-scheme"), 1, "Empty path")); return; } + /* Strip query parameters (?...) from the path. Web pages and + * SharedWorkers may append ?v=... or other cache-busting params. */ + char *path = g_strdup(path_start); + char *qmark = strchr(path, '?'); + if (qmark) *qmark = '\0'; + + /* Resolve root-relative paths against the requesting page's directory. + * If the path is an absolute filesystem path (e.g. /home/user/...), + * use it as-is. If it's a root-relative path (e.g. /nostr-login-lite/...) + * that doesn't exist, try resolving it relative to the page's directory. */ + if (path[0] == '/' && !g_file_test(path, G_FILE_TEST_EXISTS)) { + WebKitWebView *wv = webkit_uri_scheme_request_get_web_view(request); + if (wv) { + const char *page_uri = webkit_web_view_get_uri(wv); + if (page_uri && g_str_has_prefix(page_uri, "local://")) { + /* Get the page's directory. */ + const char *page_path = page_uri + 8; + char *page_dir = g_strdup(page_path); + char *last_slash = strrchr(page_dir, '/'); + if (last_slash) { + *(last_slash + 1) = '\0'; + char *resolved = g_strconcat(page_dir, path + 1, NULL); + if (g_file_test(resolved, G_FILE_TEST_EXISTS)) { + g_print("[local-scheme] Resolved root-relative %s -> %s\n", + path, resolved); + g_free(path); + path = resolved; + } else { + g_free(resolved); + } + } + g_free(page_dir); + } + } + } + /* Read the file. */ gsize length = 0; char *content = NULL; @@ -104,6 +140,7 @@ static void on_local_scheme(WebKitURISchemeRequest *request, gpointer user_data) if (!g_file_get_contents(path, &content, &length, &error)) { g_printerr("[local-scheme] Failed to read %s: %s\n", path, error ? error->message : "unknown error"); + g_free(path); if (error) g_error_free(error); webkit_uri_scheme_request_finish_error(request, g_error_new_literal( g_quark_from_static_string("local-scheme"), 2, "File not found")); @@ -114,6 +151,7 @@ static void on_local_scheme(WebKitURISchemeRequest *request, gpointer user_data) const char *mime_type = guess_mime(path); g_print("[local-scheme] Serving %s (%s, %lu bytes)\n", path, mime_type, (unsigned long)length); + g_free(path); /* Wrap the content in a GInputStream. g_memory_input_stream_new_from_data * takes ownership of the data via the GDestroyNotify callback. */ diff --git a/src/version.h b/src/version.h index c566128..ac3f3ad 100644 --- a/src/version.h +++ b/src/version.h @@ -11,9 +11,9 @@ #ifndef SOVEREIGN_BROWSER_VERSION_H #define SOVEREIGN_BROWSER_VERSION_H -#define SB_VERSION "v0.0.66" +#define SB_VERSION "v0.0.67" #define SB_VERSION_MAJOR 0 #define SB_VERSION_MINOR 0 -#define SB_VERSION_PATCH 66 +#define SB_VERSION_PATCH 67 #endif /* SOVEREIGN_BROWSER_VERSION_H */ diff --git a/tests/local-site/shared-worker-import-test.html b/tests/local-site/shared-worker-import-test.html new file mode 100644 index 0000000..b5433e5 --- /dev/null +++ b/tests/local-site/shared-worker-import-test.html @@ -0,0 +1,53 @@ + + + + + +SharedWorker importScripts Test + + + +

🔄 SharedWorker importScripts Test

+

Tests whether importScripts() works inside a SharedWorker loaded from local://.

+ +
Running tests...
+ + + + diff --git a/tests/local-site/shared-worker-import-test.js b/tests/local-site/shared-worker-import-test.js new file mode 100644 index 0000000..072d2dd --- /dev/null +++ b/tests/local-site/shared-worker-import-test.js @@ -0,0 +1,31 @@ +/** + * SharedWorker test for importScripts() on local://. + * Tests whether importScripts() can load scripts from the local:// scheme. + */ +console.log('[Import-Test] Worker script executing'); +console.log('[Import-Test] self.location.href:', self.location.href); +console.log('[Import-Test] self.location.origin:', self.location.origin); + +// Try importScripts with a relative path +try { + importScripts('./shared-worker-test.js'); + console.log('[Import-Test] importScripts succeeded'); +} catch (e) { + console.log('[Import-Test] importScripts FAILED:', e.message); +} + +self.onconnect = function(e) { + var port = e.ports[0]; + port.postMessage('worker_ready'); + port.onmessage = function(e) { + if (e.data === 'ping') { + port.postMessage('pong'); + } else if (e.data === 'get_info') { + port.postMessage(JSON.stringify({ + href: self.location.href, + origin: self.location.origin, + protocol: self.location.protocol + })); + } + }; +}; diff --git a/tests/local-site/shared-worker-test.html b/tests/local-site/shared-worker-test.html new file mode 100644 index 0000000..24a0527 --- /dev/null +++ b/tests/local-site/shared-worker-test.html @@ -0,0 +1,71 @@ + + + + + +SharedWorker Test + + + +

🔄 SharedWorker Test

+

Tests whether SharedWorker works when loaded from a local:// URI.

+ +
Running tests...
+ + + + diff --git a/tests/local-site/shared-worker-test.js b/tests/local-site/shared-worker-test.js new file mode 100644 index 0000000..8f8be3c --- /dev/null +++ b/tests/local-site/shared-worker-test.js @@ -0,0 +1,29 @@ +/** + * Minimal SharedWorker test script. + * Responds to 'ping' with 'pong' and reports its own URL and origin. + */ + +console.log('[SharedWorker-Test] Worker script executing...'); +console.log('[SharedWorker-Test] self.location.href:', self.location.href); +console.log('[SharedWorker-Test] self.location.origin:', self.location.origin); +console.log('[SharedWorker-Test] self.location.protocol:', self.location.protocol); + +self.onconnect = function(e) { + var port = e.ports[0]; + console.log('[SharedWorker-Test] Connected from port'); + + port.postMessage('worker_ready'); + + port.onmessage = function(e) { + console.log('[SharedWorker-Test] Received:', e.data); + if (e.data === 'ping') { + port.postMessage('pong'); + } else if (e.data === 'get_info') { + port.postMessage(JSON.stringify({ + href: self.location.href, + origin: self.location.origin, + protocol: self.location.protocol + })); + } + }; +};