diff --git a/addons/addon-web-links/src/WebLinkProvider.ts b/addons/addon-web-links/src/WebLinkProvider.ts index 25dd983c9b..713f9c2389 100644 --- a/addons/addon-web-links/src/WebLinkProvider.ts +++ b/addons/addon-web-links/src/WebLinkProvider.ts @@ -41,6 +41,18 @@ export class WebLinkProvider implements ILinkProvider { } } +function baseUrlString(url: URL): string { + if (url.password && url.username) { + return `${url.protocol}//${url.username}:${url.password}@${url.host}`; + } + + if (url.username) { + return `${url.protocol}//${url.username}@${url.host}`; + } + + return `${url.protocol}//${url.host}`; +} + export class LinkComputer { public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] { const rex = new RegExp(regex.source, (regex.flags || '') + 'g'); @@ -56,16 +68,11 @@ export class LinkComputer { // check via URL if the matched text would form a proper url // NOTE: This outsources the ugly url parsing to the browser. - // To avoid surprising auto expansion from URL we additionally - // check afterwards if the provided string resembles the parsed - // one close enough: - // - decodeURI decode path segement back to byte repr - // to detect unicode auto conversion correctly - // - append / also match domain urls w'o any path notion + // we check if the provided string resembles the URL-parsed one + // up to the end of the domain name (ignoring path and params) try { const url = new URL(text); - const urlText = decodeURI(url.toString()); - if (text !== urlText && text + '/' !== urlText) { + if (!text.startsWith(baseUrlString(url))) { continue; } } catch (e) { diff --git a/addons/addon-web-links/test/WebLinksAddon.api.ts b/addons/addon-web-links/test/WebLinksAddon.api.ts index fb5be20bac..5b05ce2e71 100644 --- a/addons/addon-web-links/test/WebLinksAddon.api.ts +++ b/addons/addon-web-links/test/WebLinksAddon.api.ts @@ -115,6 +115,13 @@ describe('WebLinksAddon', () => { await resetAndHover(5, 1); await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); }); + it('url encoded params work properly', async () => { + await writeSync(page, '¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3'); + await resetAndHover(12, 0); + await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); + await resetAndHover(5, 1); + await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); + }); }); });