Theseus: preserve query on link-navigation intercept + immediate address-bar reflect

Two related navigation bugs, both surfacing when a page inside a tab
tries to submit a search:

1. will-navigate rewriter dropped the query string and fragment.
   Every dotted host went through
     navigateTab(id, parsed.hostname + parsed.pathname)
   which stripped ?q=... The classic-form-GET search engines
   (Google, Brave, Bing, Startpage, Yandex, Ecosia, Mojeek, ...) all
   silently landed on their /search endpoint with no query, so no
   results ever showed. DuckDuckGo only appeared to work because its
   in-page search uses history.pushState + XHR and never triggered
   will-navigate to begin with.
   Fix: pass the whole URL (minus scheme) so query + fragment survive.

2. Address bar showed the previous page's URL until the new page's
   did-navigate fired. navigateTab called setLoading() -> emitTabs()
   BEFORE assigning t.url, so the chrome renderer received the stale
   URL and painted it (goURL() blurs the input on Enter, so the
   "don't clobber typed text" guard didn't skip the write).
   Fix: assign t.url from host+rest immediately, before setLoading.
This commit is contained in:
Local Dev 2026-08-29 22:22:47 +02:00
parent 78d20d6197
commit 574c0ed59a

16
main.js
View file

@ -1255,7 +1255,14 @@ function createTab(initial, opts = {}) {
return navigateTab(id, target + rest);
}
if (parsed.protocol === "bns:") return;
if (isBnsHost(parsed.hostname)) { e.preventDefault(); navigateTab(id, parsed.hostname + parsed.pathname); }
if (isBnsHost(parsed.hostname)) {
// Preserve query + fragment. Dropping them broke every search engine
// that submits via a classic form GET (Google's /search?q=foo lost
// the ?q=, so the results page opened blank). DuckDuckGo only
// appeared to work because its search is a client-side pushState.
e.preventDefault();
navigateTab(id, u.replace(/^[a-z]+:\/\//i, ""));
}
} catch {}
});
// "You have unsaved changes" confirmation: fires when the page's beforeunload
@ -1381,13 +1388,18 @@ async function navigateTab(id, input) {
const t = tabById(id); if (!t) return;
let q = String(input).trim();
if (!q) return;
setLoading(t, true); // show the loading indicator immediately (covers BNS resolution)
// Address bar doubles as a search box: anything that isn't a URL/hostname
// (a bare word, or a phrase with spaces) becomes a web search.
if (!looksLikeUrl(q)) q = SEARCH(q);
const raw = q.replace(/^[a-z]+:\/\//i, "");
const host = raw.split("/")[0].toLowerCase();
const rest = raw.slice(host.length) || "/";
// Reflect the target URL immediately so the address bar doesn't keep
// showing the previous page's URL for the whole load duration. Without
// this, emitTabs below (triggered by setLoading) carries the old t.url
// and the chrome renderer paints it, since we blurred the input on Enter.
t.url = "https://" + host + (rest === "/" ? "" : rest);
setLoading(t, true); // show the loading indicator immediately (covers BNS resolution)
t.nav = (t.nav || 0) + 1;
// Legacy "also on BCNR" chip state — kept clean; the passive switch is gone