Description
Tampermonkey compatibility gap in GM_download (and GM.download): calling GM_download with an empty url string ("") behaves differently from Tampermonkey.
- Tampermonkey: fails fast — either throws synchronously or invokes
onerror.
- ScriptCat: neither
onerror fires nor does it throw. Instead, "" is resolved via new URL("", location.href), which per the WHATWG URL spec resolves to the current page's own URL — so ScriptCat silently "downloads" the current page and fires onload instead.
Reproduction
// ==UserScript==
// @name GM_download empty URL test
// @match *://*/*?GM_download_empty_URL
// @grant GM_download
// ==/UserScript==
(() => {
let done = false;
try {
GM_download({
url: "",
name: "empty-url-test.bin",
onload: () => finish("FAIL: onload fired"),
onerror: e => finish("PASS: onerror", e),
});
} catch (e) {
finish("PASS: threw", e);
}
setTimeout(() => finish("FAIL: neither onerror nor throw"), 3000);
function finish(result, detail) {
if (done) return;
done = true;
console.log(`[empty URL] ${result}`, detail ?? "");
alert(`[empty URL] ${result}`);
}
})();
Install this script, visit any matching page. Under Tampermonkey it alerts PASS: onerror (or PASS: threw). Under ScriptCat it alerts FAIL: onload fired — the current page gets "downloaded" as empty-url-test.bin.
Root cause
src/app/service/content/gm_api/gm_xhr.ts resolves the URL with new URL(urlResolved, window.location.href). When urlResolved is "", this does not throw; it resolves to window.location.href itself. There is no explicit empty/falsy url guard before this in src/app/service/content/gm_api/gm_api.ts's _GM_download, so an empty url silently falls through to the native XHR/download path instead of failing.
Expected behavior
GM_download({ url: "" , ... }) should invoke details.onerror (and reject the GM.download promise) without issuing any network request or download, matching Tampermonkey.
Description
Tampermonkey compatibility gap in
GM_download(andGM.download): callingGM_downloadwith an emptyurlstring ("") behaves differently from Tampermonkey.onerror.onerrorfires nor does it throw. Instead,""is resolved vianew URL("", location.href), which per the WHATWG URL spec resolves to the current page's own URL — so ScriptCat silently "downloads" the current page and firesonloadinstead.Reproduction
Install this script, visit any matching page. Under Tampermonkey it alerts
PASS: onerror(orPASS: threw). Under ScriptCat it alertsFAIL: onload fired— the current page gets "downloaded" asempty-url-test.bin.Root cause
src/app/service/content/gm_api/gm_xhr.tsresolves the URL withnew URL(urlResolved, window.location.href). WhenurlResolvedis"", this does not throw; it resolves towindow.location.hrefitself. There is no explicit empty/falsyurlguard before this insrc/app/service/content/gm_api/gm_api.ts's_GM_download, so an emptyurlsilently falls through to the native XHR/download path instead of failing.Expected behavior
GM_download({ url: "" , ... })should invokedetails.onerror(and reject theGM.downloadpromise) without issuing any network request or download, matching Tampermonkey.