UNPKG

xterm

Version:

Full xterm terminal, in your browser

147 lines (129 loc) 4.05 kB
/** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from 'common/Types'; import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType } from 'common/parser/Types'; import { utf32ToString } from 'common/input/TextDecoder'; import { Params } from 'common/parser/Params'; import { PAYLOAD_LIMIT } from 'common/parser/Constants'; const EMPTY_HANDLERS: IDcsHandler[] = []; export class DcsParser implements IDcsParser { private _handlers: IHandlerCollection<IDcsHandler> = Object.create(null); private _active: IDcsHandler[] = EMPTY_HANDLERS; private _ident: number = 0; private _handlerFb: DcsFallbackHandlerType = () => {}; public dispose(): void { this._handlers = Object.create(null); this._handlerFb = () => {}; } public addHandler(ident: number, handler: IDcsHandler): IDisposable { if (this._handlers[ident] === undefined) { this._handlers[ident] = []; } const handlerList = this._handlers[ident]; handlerList.push(handler); return { dispose: () => { const handlerIndex = handlerList.indexOf(handler); if (handlerIndex !== -1) { handlerList.splice(handlerIndex, 1); } } }; } public setHandler(ident: number, handler: IDcsHandler): void { this._handlers[ident] = [handler]; } public clearHandler(ident: number): void { if (this._handlers[ident]) delete this._handlers[ident]; } public setHandlerFallback(handler: DcsFallbackHandlerType): void { this._handlerFb = handler; } public reset(): void { if (this._active.length) { this.unhook(false); } this._active = EMPTY_HANDLERS; this._ident = 0; } public hook(ident: number, params: IParams): void { // always reset leftover handlers this.reset(); this._ident = ident; this._active = this._handlers[ident] || EMPTY_HANDLERS; if (!this._active.length) { this._handlerFb(this._ident, 'HOOK', params); } else { for (let j = this._active.length - 1; j >= 0; j--) { this._active[j].hook(params); } } } public put(data: Uint32Array, start: number, end: number): void { if (!this._active.length) { this._handlerFb(this._ident, 'PUT', utf32ToString(data, start, end)); } else { for (let j = this._active.length - 1; j >= 0; j--) { this._active[j].put(data, start, end); } } } public unhook(success: boolean): void { if (!this._active.length) { this._handlerFb(this._ident, 'UNHOOK', success); } else { let j = this._active.length - 1; for (; j >= 0; j--) { if (this._active[j].unhook(success) !== false) { break; } } j--; // cleanup left over handlers for (; j >= 0; j--) { this._active[j].unhook(false); } } this._active = EMPTY_HANDLERS; this._ident = 0; } } /** * Convenient class to create a DCS handler from a single callback function. * Note: The payload is currently limited to 50 MB (hardcoded). */ export class DcsHandler implements IDcsHandler { private _data = ''; private _params: IParams | undefined; private _hitLimit: boolean = false; constructor(private _handler: (data: string, params: IParams) => any) {} public hook(params: IParams): void { this._params = params.clone(); this._data = ''; this._hitLimit = false; } public put(data: Uint32Array, start: number, end: number): void { if (this._hitLimit) { return; } this._data += utf32ToString(data, start, end); if (this._data.length > PAYLOAD_LIMIT) { this._data = ''; this._hitLimit = true; } } public unhook(success: boolean): any { let ret; if (this._hitLimit) { ret = false; } else if (success) { ret = this._handler(this._data, this._params ? this._params : new Params()); } this._params = undefined; this._data = ''; this._hitLimit = false; return ret; } }