diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 8f9a988f14..0f17a3e69a 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -12,11 +12,12 @@ import { Attributes, BgFlags, UnderlineStyle } from 'common/buffer/Constants'; import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService, MockOscLinkService } from 'common/TestUtils.test'; -import { IBufferService, ICoreService } from 'common/services/Services'; +import { IBufferService, ICoreService, type IOscLinkService } from 'common/services/Services'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { clone } from 'common/Clone'; import { BufferService } from 'common/services/BufferService'; import { CoreService } from 'common/services/CoreService'; +import { OscLinkService } from 'common/services/OscLinkService'; function getCursor(bufferService: IBufferService): number[] { @@ -59,6 +60,7 @@ describe('InputHandler', () => { let bufferService: IBufferService; let coreService: ICoreService; let optionsService: MockOptionsService; + let oscLinkService: IOscLinkService; let inputHandler: TestInputHandler; beforeEach(() => { @@ -66,8 +68,9 @@ describe('InputHandler', () => { bufferService = new BufferService(optionsService); bufferService.resize(80, 30); coreService = new CoreService(bufferService, new MockLogService(), optionsService); + oscLinkService = new OscLinkService(bufferService); - inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockLogService(), optionsService, new MockOscLinkService(), new MockCoreMouseService(), new MockUnicodeService()); + inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockLogService(), optionsService, oscLinkService, new MockCoreMouseService(), new MockUnicodeService()); }); describe('SL/SR/DECIC/DECDC', () => { @@ -1982,6 +1985,32 @@ describe('InputHandler', () => { assert.deepEqual(stack, [[{ type: ColorRequestType.SET, index: 0, color: [170, 187, 204] }, { type: ColorRequestType.SET, index: 123, color: [0, 17, 34] }]]); stack.length = 0; }); + it('8: hyperlink with id', async () => { + await inputHandler.parseP('\x1b]8;id=100;http://localhost:3000\x07'); + assert.notStrictEqual(inputHandler.curAttrData.extended.urlId, 0); + assert.deepStrictEqual( + oscLinkService.getLinkData(inputHandler.curAttrData.extended.urlId), + { + id: '100', + uri: 'http://localhost:3000' + } + ); + await inputHandler.parseP('\x1b]8;;\x07'); + assert.strictEqual(inputHandler.curAttrData.extended.urlId, 0); + }); + it('8: hyperlink with semi-colon', async () => { + await inputHandler.parseP('\x1b]8;;http://localhost:3000;abc=def\x07'); + assert.notStrictEqual(inputHandler.curAttrData.extended.urlId, 0); + assert.deepStrictEqual( + oscLinkService.getLinkData(inputHandler.curAttrData.extended.urlId), + { + id: undefined, + uri: 'http://localhost:3000;abc=def' + } + ); + await inputHandler.parseP('\x1b]8;;\x07'); + assert.strictEqual(inputHandler.curAttrData.extended.urlId, 0); + }); it('104: restore events', async () => { const stack: IColorEvent[] = []; inputHandler.onColor(ev => stack.push(ev)); diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 9b300993d3..6db8751ea6 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -2972,14 +2972,18 @@ export class InputHandler extends Disposable implements IInputHandler { * feedback. Use `OSC 8 ; ; BEL` to finish the current hyperlink. */ public setHyperlink(data: string): boolean { - const args = data.split(';'); - if (args.length < 2) { - return false; + // Arg parsing is special cases to support unencoded semi-colons in the URIs (#4944) + const idx = data.indexOf(';'); + if (idx === -1) { + // malformed sequence, just return as handled + return true; } - if (args[1]) { - return this._createHyperlink(args[0], args[1]); + const id = data.slice(0, idx).trim(); + const uri = data.slice(idx + 1); + if (uri) { + return this._createHyperlink(id, uri); } - if (args[0].trim()) { + if (id.trim()) { return false; } return this._finishHyperlink();