From 57dfc1ecc6c8dddc42faafefc1aade2f75ebdb24 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Apr 2025 11:14:25 +0200 Subject: [PATCH] Because we're no longer using document.execCommand('paste'), we don't need a textarea anymore --- .../editContext/native/nativeEditContext.ts | 53 +------------------ .../native/nativeEditContextUtils.ts | 16 ------ .../contrib/clipboard/browser/clipboard.ts | 20 +++---- 3 files changed, 8 insertions(+), 81 deletions(-) diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts index 38d9dac777e..e262b02c21c 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -30,7 +30,6 @@ import { EditContext } from './editContextFactory.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { NativeEditContextRegistry } from './nativeEditContextRegistry.js'; import { IEditorAriaOptions } from '../../../editorBrowser.js'; -import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; import { isHighSurrogate, isLowSurrogate } from '../../../../../base/common/strings.js'; // Corresponds to classes in nativeEditContext.css @@ -51,7 +50,6 @@ interface ITextUpdateEvent { export class NativeEditContext extends AbstractEditContext { // Text area used to handle paste events - private readonly _textArea: FastDomNode; public readonly domNode: FastDomNode; private readonly _editContext: EditContext; private readonly _screenReaderSupport: ScreenReaderSupport; @@ -79,15 +77,11 @@ export class NativeEditContext extends AbstractEditContext { private readonly _visibleRangeProvider: IVisibleRangeProvider, @IInstantiationService instantiationService: IInstantiationService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, - @IClipboardService private readonly _clipboardService: IClipboardService, ) { super(context); this.domNode = new FastDomNode(document.createElement('div')); this.domNode.setClassName(`native-edit-context`); - this._textArea = new FastDomNode(document.createElement('textarea')); - this._textArea.setClassName('native-edit-context-textarea'); - this._textArea.setAttribute('tabindex', '-1'); this.domNode.setAttribute('autocorrect', 'off'); this.domNode.setAttribute('autocapitalize', 'off'); this.domNode.setAttribute('autocomplete', 'off'); @@ -96,7 +90,6 @@ export class NativeEditContext extends AbstractEditContext { this._updateDomAttributes(); overflowGuardContainer.appendChild(this.domNode); - overflowGuardContainer.appendChild(this._textArea); this._parent = overflowGuardContainer.domNode; this._selectionChangeListener = this._register(new MutableDisposable()); @@ -183,31 +176,6 @@ export class NativeEditContext extends AbstractEditContext { // Emits ViewCompositionEndEvent which can be depended on by ViewEventHandlers this._context.viewModel.onCompositionEnd(); })); - this._register(addDisposableListener(this._textArea.domNode, 'paste', (e) => { - // Pretend here we touched the text area, as the `paste` event will most likely - // result in a `selectionchange` event which we want to ignore - this._screenReaderSupport.setIgnoreSelectionChangeTime('onPaste'); - e.preventDefault(); - if (!e.clipboardData) { - return; - } - let [text, metadata] = ClipboardEventUtils.getTextData(e.clipboardData); - if (!text) { - return; - } - metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text); - let pasteOnNewLine = false; - let multicursorText: string[] | null = null; - let mode: string | null = null; - if (metadata) { - const options = this._context.configuration.options; - const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); - pasteOnNewLine = emptySelectionClipboard && !!metadata.isFromEmptySelection; - multicursorText = typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null; - mode = metadata.mode; - } - viewController.paste(text, pasteOnNewLine, multicursorText, mode); - })); this._register(NativeEditContextRegistry.register(ownerID, this)); } @@ -217,7 +185,6 @@ export class NativeEditContext extends AbstractEditContext { // Force blue the dom node so can write in pane with no native edit context after disposal this.domNode.domNode.blur(); this.domNode.domNode.remove(); - this._textArea.domNode.remove(); super.dispose(); } @@ -285,26 +252,8 @@ export class NativeEditContext extends AbstractEditContext { return true; } - public triggerPaste(): Promise | undefined { + public onWillPaste(): void { this._onWillPaste(); - // pause focus tracking because we don't want to react to focus/blur - // events while pasting since we move the focus to the textarea - this._focusTracker.pause(); - - // Since we can not call execCommand('paste') on a dom node with edit context set - // we added a hidden text area that receives the paste execution - this._textArea.focus(); - const triggerPaste = this._clipboardService.triggerPaste(); - if (!triggerPaste) { - this.domNode.domNode.focus(); - this._focusTracker.resume(); // resume focus tracking - return undefined; - } - return triggerPaste.then(() => { - this._textArea.domNode.textContent = ''; - this.domNode.domNode.focus(); - this._focusTracker.resume(); // resume focus tracking - }); } private _onWillPaste(): void { diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts index 90f3e9839be..faa0ec2f2cc 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts @@ -15,7 +15,6 @@ export interface ITypeData { export class FocusTracker extends Disposable { private _isFocused: boolean = false; - private _isPaused: boolean = false; constructor( private readonly _domNode: HTMLElement, @@ -23,31 +22,16 @@ export class FocusTracker extends Disposable { ) { super(); this._register(addDisposableListener(this._domNode, 'focus', () => { - if (this._isPaused) { - return; - } // Here we don't trust the browser and instead we check // that the active element is the one we are tracking // (this happens when cmd+tab is used to switch apps) this.refreshFocusState(); })); this._register(addDisposableListener(this._domNode, 'blur', () => { - if (this._isPaused) { - return; - } this._handleFocusedChanged(false); })); } - public pause(): void { - this._isPaused = true; - } - - public resume(): void { - this._isPaused = false; - this.refreshFocusState(); - } - private _handleFocusedChanged(focused: boolean): void { if (this._isFocused === focused) { return; diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts index 3f25e415aea..ea1ed6442e6 100644 --- a/src/vs/editor/contrib/clipboard/browser/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -238,21 +238,15 @@ if (PasteAction) { if (experimentalEditContextEnabled) { const nativeEditContext = NativeEditContextRegistry.get(focusedEditor.getId()); if (nativeEditContext) { - const triggerPaste = nativeEditContext.triggerPaste(); - if (triggerPaste) { - return triggerPaste.then(async () => { - return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve(); - }); - } - } - } else { - const triggerPaste = clipboardService.triggerPaste(); - if (triggerPaste) { - return triggerPaste.then(async () => { - return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve(); - }); + nativeEditContext.onWillPaste(); } } + const triggerPaste = clipboardService.triggerPaste(); + if (triggerPaste) { + return triggerPaste.then(async () => { + return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve(); + }); + } if (platform.isWeb) { // Use the clipboard service if document.execCommand('paste') was not successful return (async () => {