This commit is contained in:
Ayman Bagabas
2024-04-08 04:40:17 +03:00
parent 94648eb4e9
commit fad97bc4d5
11 changed files with 8 additions and 290 deletions
+2 -25
View File
@@ -46,7 +46,7 @@ import { CoreTerminal } from 'common/CoreTerminal';
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
import * as Browser from 'common/Platform';
import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IClipboardEvent, IColorEvent, ITerminalOptions, KeyboardResultType, ScrollSource, SpecialColorIndex } from 'common/Types';
import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, ScrollSource, SpecialColorIndex } from 'common/Types';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { IBuffer } from 'common/buffer/Types';
import { C0, C1_ESCAPED } from 'common/data/EscapeSequences';
@@ -54,7 +54,7 @@ import { evaluateKeyboardEvent } from 'common/input/Keyboard';
import { toRgbString } from 'common/input/XParseColor';
import { DecorationService } from 'common/services/DecorationService';
import { IDecorationService } from 'common/services/Services';
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker, IClipboardProvider } from '@xterm/xterm';
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
import { WindowsOptionsReportType } from '../common/InputHandler';
import { AccessibilityManager } from './AccessibilityManager';
import { LinkProviderService } from 'browser/services/LinkProviderService';
@@ -121,7 +121,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
public viewport: IViewport | undefined;
private _compositionHelper: ICompositionHelper | undefined;
private _accessibilityManager: MutableDisposable<AccessibilityManager> = this.register(new MutableDisposable());
private _clipboardProvider: IClipboardProvider | undefined;
private readonly _onCursorMove = this.register(new EventEmitter<void>());
public readonly onCursorMove = this._onCursorMove.event;
@@ -167,7 +166,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this._inputHandler.onRequestReset(() => this.reset()));
this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
this.register(this._inputHandler.onColor((event) => this._handleColorEvent(event)));
this.register(this._inputHandler.onClipboard((event) => this._handleClipboardEvent(event)));
this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));
this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));
this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
@@ -905,15 +903,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
return this._linkProviderService.registerLinkProvider(linkProvider);
}
public registerClipboardProvider(provider: IClipboardProvider): IDisposable {
this._clipboardProvider = provider;
return {
dispose: () => {
this._clipboardProvider = undefined;
}
};
}
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
if (!this._characterJoinerService) {
throw new Error('Terminal must be opened first');
@@ -1314,18 +1303,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
}
}
private _handleClipboardEvent(ev: IClipboardEvent): void {
if (!this._clipboardProvider) {
return;
}
if (ev.data === '?') {
this._clipboardProvider.readText(ev.selection).then(data =>
this.coreService.triggerDataEvent(data));
return;
}
this._clipboardProvider.writeText(ev.selection, ev.data);
}
// TODO: Remove cancel function and cancelEvents option
public cancel(ev: Event, force?: boolean): boolean | undefined {
if (!this.options.cancelEvents && !force) {
+1 -4
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, IClipboardProvider } from '@xterm/xterm';
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
@@ -111,9 +111,6 @@ export class MockTerminal implements ITerminal {
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
throw new Error('Method not implemented.');
}
public registerClipboardProvider(provider: IClipboardProvider): IDisposable {
throw new Error('Method not implemented.');
}
public hasSelection(): boolean {
throw new Error('Method not implemented.');
}
+1 -4
View File
@@ -13,7 +13,7 @@ import { AddonManager } from 'common/public/AddonManager';
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
import { ParserApi } from 'common/public/ParserApi';
import { UnicodeApi } from 'common/public/UnicodeApi';
import { IBufferNamespace as IBufferNamespaceApi, IClipboardProvider, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
/**
* The set of options that only have an effect when set in the Terminal constructor.
@@ -174,9 +174,6 @@ export class Terminal extends Disposable implements ITerminalApi {
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
return this._core.registerDecoration(decorationOptions);
}
public registerClipboardProvider(provider: IClipboardProvider): IDisposable {
return this._core.registerClipboardProvider(provider);
}
public hasSelection(): boolean {
return this._core.hasSelection();
}
+1 -84
View File
@@ -5,7 +5,7 @@
import { assert } from 'chai';
import { InputHandler } from 'common/InputHandler';
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType, SpecialColorIndex, IClipboardEvent, ClipboardEventType } from 'common/Types';
import { IBufferLine, IAttributeData, IColorEvent, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { Attributes, BgFlags, UnderlineStyle } from 'common/buffer/Constants';
@@ -17,7 +17,6 @@ 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 { ClipboardSelectionType } from '@xterm/xterm';
function getCursor(bufferService: IBufferService): number[] {
return [
@@ -1982,88 +1981,6 @@ 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;
});
describe('52: manipulate selection data', async () => {
const testDataRaw = 'hello world';
const testDataB64 = 'aGVsbG8gd29ybGQ=';
optionsService.options.allowClipboardAccess = true;
const stack: IClipboardEvent[] = [];
inputHandler.onClipboard(ev => stack.push(ev));
await inputHandler.parseP(`\x1b]52;c;\x07`);
await inputHandler.parseP(`\x1b]52;c;${testDataRaw}\x07`);
await inputHandler.parseP(`\x1b]52;c;${testDataB64}\x07`);
await inputHandler.parseP(`\x1b]52;c;${testDataB64}invalid\x07`);
await inputHandler.parseP(`\x1b]52;c;!\x07`);
await inputHandler.parseP(`\x1b]52;c;?\x07`);
await inputHandler.parseP(`\x1b]52;p;\x07`);
await inputHandler.parseP(`\x1b]52;p;${testDataRaw}\x07`);
await inputHandler.parseP(`\x1b]52;p;${testDataB64}\x07`);
await inputHandler.parseP(`\x1b]52;p;${testDataB64}invalid\x07`);
await inputHandler.parseP(`\x1b]52;p;!\x07`);
await inputHandler.parseP(`\x1b]52;p;?\x07`);
assert.deepEqual(stack, [
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.SYSTEM,
data: ''
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.SYSTEM,
data: testDataRaw
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.SYSTEM,
data: testDataB64
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.SYSTEM,
data: testDataB64+'invalid'
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.SYSTEM,
data: '!'
},
{
type: ClipboardEventType.REPORT,
selection: ClipboardSelectionType.SYSTEM,
data: '?'
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.PRIMARY,
data: ''
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.PRIMARY,
data: testDataRaw
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.PRIMARY,
data: testDataB64
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.PRIMARY,
data: testDataB64+'invalid'
},
{
type: ClipboardEventType.SET,
selection: ClipboardSelectionType.PRIMARY,
data: '!'
},
{
type: ClipboardEventType.REPORT,
selection: ClipboardSelectionType.PRIMARY,
data: '?'
}
]);
stack.length = 0;
});
it('104: restore events', async () => {
const stack: IColorEvent[] = [];
inputHandler.onColor(ev => stack.push(ev));
+1 -59
View File
@@ -4,7 +4,7 @@
* @license MIT
*/
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex, IClipboardEvent, ClipboardEventType } from 'common/Types';
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions, IColorEvent, IParseStack, ColorIndex, ColorRequestType, SpecialColorIndex } from 'common/Types';
import { C0, C1 } from 'common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
@@ -22,7 +22,6 @@ import { OscHandler } from 'common/parser/OscParser';
import { DcsHandler } from 'common/parser/DcsParser';
import { IBuffer } from 'common/buffer/Types';
import { parseColor } from 'common/input/XParseColor';
import { ClipboardSelectionType } from '@xterm/xterm';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -160,8 +159,6 @@ export class InputHandler extends Disposable implements IInputHandler {
public readonly onTitleChange = this._onTitleChange.event;
private readonly _onColor = this.register(new EventEmitter<IColorEvent>());
public readonly onColor = this._onColor.event;
private readonly _onClipboard = this.register(new EventEmitter<IClipboardEvent>());
public readonly onClipboard = this._onClipboard.event;
private _parseStack: IParseStack = {
paused: false,
@@ -323,7 +320,6 @@ export class InputHandler extends Disposable implements IInputHandler {
// 50 - Set Font to Pt.
// 51 - reserved for Emacs shell.
// 52 - Manipulate Selection Data.
this._parser.registerOscHandler(52, new OscHandler(data => this.setOrReportClipboard(data)));
// 104 ; c - Reset Color Number c.
this._parser.registerOscHandler(104, new OscHandler(data => this.restoreIndexedColor(data)));
// 105 ; c - Reset Special Color Number c.
@@ -3081,60 +3077,6 @@ export class InputHandler extends Disposable implements IInputHandler {
return this._setOrReportSpecialColor(data, 2);
}
private _setOrReportClipboard(data: string): boolean {
if (!this._optionsService.options.allowClipboardAccess) {
return true;
}
const args = data.split(';');
if (args.length < 2) {
return true;
}
const pc = args[0];
const pd = args[1];
if (pd.length === 0) {
return true;
}
switch (pc) {
case ClipboardSelectionType.SYSTEM:
case ClipboardSelectionType.PRIMARY:
this._onClipboard.fire({
type: pd === '?' ? ClipboardEventType.REPORT : ClipboardEventType.SET,
selection: pc,
data: pd
});
break;
}
return true;
}
/**
* OSC 52 ; <selection name> ; <base64 data>|<?> ST - set or query selection and clipboard data
*
* Test case:
*
* ```sh
* printf "\e]52;c;%s\a" "$(echo -n "Hello, World" | base64)"
* ```
*
* @vt: #Y OSC 52 "Manipulate Selection Data" "OSC 52 ; Pc ; Pd BEL" "Set or query selection and clipboard data."
* Pc is the selection name. Can be one of:
* - `c` - clipboard
* - `p` - primary
* - `q` - secondary
* - `s` - select
* - `0-7` - cut-buffers 0-7
*
* Only the `c` selection (clipboard) is supported by xterm.js. The browser
* Clipboard API only supports the clipboard selection.
*
* Pd is the base64 encoded data.
* If Pd is `?`, the terminal returns the current clipboard contents.
* If Pd is neither base64 encoded nor `?`, then the clipboard is cleared.
*/
public setOrReportClipboard(data: string): boolean {
return this._setOrReportClipboard(data);
}
/**
* OSC 104 ; <num> ST - restore ANSI color <num>
*
+1 -13
View File
@@ -9,7 +9,7 @@ import { Attributes, UnderlineStyle } from 'common/buffer/Constants'; // eslint-
import { IBufferSet } from 'common/buffer/Types';
import { IParams } from 'common/parser/Types';
import { ICoreMouseService, ICoreService, IOptionsService, IUnicodeService } from 'common/services/Services';
import { ClipboardSelectionType as ClipboardSelectionType, IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm';
import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm';
export interface ICoreTerminal {
coreMouseService: ICoreMouseService;
@@ -447,17 +447,6 @@ export interface IColorRestoreRequest {
}
export type IColorEvent = (IColorReportRequest | IColorSetRequest | IColorRestoreRequest)[];
export const enum ClipboardEventType {
REPORT = 0,
SET = 1
}
export interface IClipboardEvent {
type: ClipboardEventType;
selection: ClipboardSelectionType;
data: string;
}
/**
* Calls the parser and handles actions generated by the parser.
*/
@@ -527,7 +516,6 @@ export interface IInputHandler {
/** OSC 10 */ setOrReportFgColor(data: string): boolean;
/** OSC 11 */ setOrReportBgColor(data: string): boolean;
/** OSC 12 */ setOrReportCursorColor(data: string): boolean;
/** OSC 52 */ setOrReportClipboard(data: string): boolean;
/** OSC 104 */ restoreIndexedColor(data: string): boolean;
/** OSC 110 */ restoreFgColor(data: string): boolean;
/** OSC 111 */ restoreBgColor(data: string): boolean;
+1 -2
View File
@@ -54,8 +54,7 @@ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
convertEol: false,
termName: 'xterm',
cancelEvents: false,
overviewRulerWidth: 0,
allowClipboardAccess: false
overviewRulerWidth: 0
};
const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
-1
View File
@@ -206,7 +206,6 @@ export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '50
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'off';
export interface ITerminalOptions {
allowClipboardAccess?: boolean;
allowProposedApi?: boolean;
allowTransparency?: boolean;
altClickMovesCursor?: boolean;
-54
View File
@@ -770,60 +770,6 @@ test.describe('API Integration Tests', () => {
});
});
test.describe('registerClipboardProvider', () => {
async function registerClipboardProvider(ctx: ITestContext): Promise<void> {
await ctx.page.evaluate(`window.clipboard = ''`);
await ctx.page.evaluate(`window.term._disposables.push(
window.term.registerClipboardProvider({
readText: (selection) => {
return Promise.resolve(window.clipboard);
},
writeText: (selection, text) => {
window.clipboard = text;
return Promise.resolve();
}
})
)`);
}
test('should register clipboard provider', async () => {
await openTerminal(ctx, { allowClipboardAccess: true });
await registerClipboardProvider(ctx);
await ctx.page.evaluate(`window.term.dispose()`);
});
test('should ignore clipboard when no provider is registered', async () => {
await openTerminal(ctx, { allowClipboardAccess: true });
await ctx.proxy.write('\x1b]52;c;foobar\x07');
strictEqual(await ctx.page.evaluate(`window.clipboard`), '');
await ctx.page.evaluate(`window.term.dispose()`);
});
test('should ignore clipboard when allowClipboardAccess is false', async () => {
await openTerminal(ctx, { allowClipboardAccess: false });
await registerClipboardProvider(ctx);
await ctx.proxy.write('\x1b]52;c;foobar\x07');
strictEqual(await ctx.page.evaluate(`window.clipboard`), '');
await ctx.page.evaluate(`window.term.dispose()`);
});
test('should save to clipboard when writeText is called', async () => {
await openTerminal(ctx, { allowClipboardAccess: true });
await registerClipboardProvider(ctx);
await ctx.proxy.write('\x1b]52;c;foobar\x07');
strictEqual(await ctx.page.evaluate(`window.clipboard`), 'foobar');
await ctx.page.evaluate(`window.term.dispose()`);
});
test('should read from clipboard when readText is called', async () => {
await openTerminal(ctx, { allowClipboardAccess: true });
await registerClipboardProvider(ctx);
await ctx.page.evaluate(`
window.data = [];
window.term.onData(e => data.push(e));
`);
await ctx.proxy.write('\x1b]52;c;foobar\x07');
await ctx.proxy.write('\x1b]52;c;?\x07');
deepStrictEqual(await ctx.page.evaluate(`window.data`), ['foobar']);
await ctx.page.evaluate(`window.term.dispose()`);
});
});
test.describe('registerLinkProvider', () => {
test('should fire provideLinks when hovering cells', async () => {
await openTerminal(ctx);
-1
View File
@@ -78,7 +78,6 @@ type TerminalProxyCustomOverrides = 'buffer' | (
'attachCustomWheelEventHandler' |
'registerLinkProvider' |
'registerCharacterJoiner' |
'registerClipboardProvider' |
'deregisterCharacterJoiner' |
'loadAddon'
);
-43
View File
@@ -24,12 +24,6 @@ declare module '@xterm/xterm' {
* An object containing options for the terminal.
*/
export interface ITerminalOptions {
/**
* Whether to allow clipboard access. When false, any access to the
* clipboard is ignored. The default is false.
*/
allowClipboardAccess?: boolean;
/**
* Whether to allow the use of proposed API. When false, any usage of APIs
* marked as experimental/proposed will throw an error. The default is
@@ -1130,14 +1124,6 @@ declare module '@xterm/xterm' {
*/
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
/**
* Registers a clipboard provider, allowing custom handling of clipboard
* selection events. This is used primarily to enable accessing the
* clipboard to read/write clipboard data.
* @param provider The provider to register.
*/
registerClipboardProvider(provider: IClipboardProvider): IDisposable;
/**
* Gets whether the terminal has an active selection.
*/
@@ -1919,33 +1905,4 @@ declare module '@xterm/xterm' {
*/
readonly wraparoundMode: boolean;
}
export interface IClipboardProvider {
/**
* Gets the clipboard content.
* @param selection The clipboard selection to read.
* @returns A promise that resolves with the base64 encoded data.
*/
readText(selection: ClipboardSelectionType): Promise<string>;
/**
* Sets the clipboard content.
* @param selection The clipboard selection to set.
* @param data The base64 encoded data to set. If the data is invalid
* base64, the clipboard is cleared.
*/
writeText(selection: ClipboardSelectionType, data: string): Promise<void>;
}
/**
* Clipboard selection type. This is used to specify which selection buffer to
* read or write to.
* - SYSTEM `c`: The system clipboard.
* - PRIMARY `p`: The primary clipboard. This is provided for compatibility
* with Linux X11.
*/
export const enum ClipboardSelectionType {
SYSTEM = 'c',
PRIMARY = 'p',
}
}