diff --git a/src/InputHandler.ts b/src/InputHandler.ts index b97152be..c1e05d53 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -296,6 +296,11 @@ export class InputHandler extends Disposable implements IInputHandler { } public parse(data: string): void { + // Ensure the terminal is not disposed + if (!this._terminal) { + return; + } + let buffer = this._terminal.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; diff --git a/src/Terminal.ts b/src/Terminal.ts index de44d1a1..8f7d27b3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1293,6 +1293,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II * @param data The text to write to the terminal. */ public write(data: string): void { + // Ensure the terminal isn't disposed + if (this._isDisposed) { + return; + } + // Ignore falsy data values (including the empty string) if (!data) { return; @@ -1321,6 +1326,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II } protected _innerWrite(): void { + // Ensure the terminal isn't disposed + if (this._isDisposed) { + this.writeBuffer = []; + } + const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); while (writeBatch.length > 0) { const data = writeBatch.shift(); diff --git a/src/common/Lifecycle.test.ts b/src/common/Lifecycle.test.ts new file mode 100644 index 00000000..4b696fa5 --- /dev/null +++ b/src/common/Lifecycle.test.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { Disposable } from './Lifecycle'; + +class TestDisposable extends Disposable { + public get isDisposed(): boolean { + return this._isDisposed; + } +} + +describe('Disposable', () => { + describe('register', () => { + it('should register disposables', () => { + const d = new TestDisposable(); + const d2 = { + dispose: () => { throw new Error(); } + }; + d.register(d2); + assert.throws(() => d.dispose()); + }); + }); + describe('unregister', () => { + it('should unregister disposables', () => { + const d = new TestDisposable(); + const d2 = { + dispose: () => { throw new Error(); } + }; + d.register(d2); + d.unregister(d2); + assert.doesNotThrow(() => d.dispose()); + }); + }); + describe('dispose', () => { + it('should set is disposed flag', () => { + const d = new TestDisposable(); + assert.isFalse(d.isDisposed); + d.dispose(); + assert.isTrue(d.isDisposed); + }); + }); +}); diff --git a/src/common/Lifecycle.ts b/src/common/Lifecycle.ts index 46828521..209a3e2a 100644 --- a/src/common/Lifecycle.ts +++ b/src/common/Lifecycle.ts @@ -11,6 +11,7 @@ import { IDisposable } from 'xterm'; */ export abstract class Disposable implements IDisposable { protected _disposables: IDisposable[] = []; + protected _isDisposed: boolean = false; constructor() { } @@ -19,6 +20,7 @@ export abstract class Disposable implements IDisposable { * Disposes the object, triggering the `dispose` method on all registered IDisposables. */ public dispose(): void { + this._isDisposed = true; this._disposables.forEach(d => d.dispose()); this._disposables.length = 0; }