xterm
Version:
Full xterm terminal, in your browser
147 lines (129 loc) • 4.05 kB
text/typescript
/**
* 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;
}
}