Skip to content

Commit be50e2d

Browse files
committed
clarify some things about URI decoding and fix an obscure potential bug
1 parent 8c5f83f commit be50e2d

File tree

2 files changed

+23
-2
lines changed

2 files changed

+23
-2
lines changed

common/src/utils.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,29 @@ export function mapGetOrInsert<K, V>(map: Map<K, V>, key: K, defaultValue: V): V
7878

7979
export function cwdFilePathToURL(path: string, base: string = document.baseURI): URL {
8080
let url = new URL(base);
81+
// Implicitly percent-encodes and doesn't trim path on the ends.
8182
url.pathname = path;
8283
return url;
8384
}
8485

8586
export function cwdFilePathFromURL(url: URL): string {
86-
return paths.stripRoot(decodeURI(url.pathname));
87+
// Why use decodeURIComponent here instead of decodeURI, you might ask? You
88+
// see, the crucial difference between the two is that decodeURIComponent
89+
// decodes every percent-encoded character without exceptions, while
90+
// decodeURI, as the ES262 specification says, does not decode "escape
91+
// sequences that could not have been introduced by encodeURI", which, in
92+
// practice, means that it doesn't decode percent-encodings of these
93+
// characters: ;/?:@&=+$,# . Now, how is this behavior useful in practice?
94+
// Perhaps the intended usecase is to make URLs look nicer by decoding stuff
95+
// like Unicode characters, yet preserving the validity and meaning of the
96+
// original URL. But it may lead to obscure bugs in handling file paths
97+
// because, albeit seldom used, all of the mentioned characters, with the
98+
// exception of slash, are valid in filenames on UNIX, and, except colon and
99+
// question mark, the rest is also valid on Windows, and so those characters
100+
// will be kept undecoded. Here are some specification links:
101+
// <https://tc39.es/ecma262/multipage/global-object.html#sec-decodeuri-encodeduri>
102+
// <https://tc39.es/ecma262/multipage/global-object.html#sec-decodeuricomponent-encodeduricomponent>
103+
return paths.stripRoot(decodeURIComponent(url.pathname));
87104
}
88105

89106
export function html<K extends keyof HTMLElementTagNameMap>(

runtime/src/resources-injections.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impactInitHooks.add(() => {
1818
if (!url.startsWith(resources.MOD_PROTOCOL_PREFIX)) {
1919
let gameAssetsURL = resources.getGameAssetsURL().href;
2020

21-
let parsedURL = new URL(encodeURI(url), document.baseURI).href;
21+
let parsedURL = new URL(url, document.baseURI).href;
2222
if (!parsedURL.startsWith(gameAssetsURL)) {
2323
return true;
2424
}
@@ -105,6 +105,10 @@ impactModuleHooks.add('impact.base.image', () => {
105105
// encodes URIs before requesting and there are no instances of leading
106106
// whitespace because `ig.root` is not empty in the development version.
107107
path = path.trimRight();
108+
// NOTE: Strictly speaking, Impact's file forwarding must be applied
109+
// first, before trimming, as in the game, but I'm not sure if this will
110+
// break anything because the current behavior certainly haven't so far,
111+
// so TODO: test this and restore the correct order.
108112
path = applyImpactFileForwarding(path);
109113
resources.loadImage(path, { callerThisValue: this }).then(
110114
(img) => {

0 commit comments

Comments
 (0)