Skip to content

Commit 8aead9b

Browse files
authored
Moving away from execCommand for pasting (#239233)
* adding code to invetsigate the clipboard * adding more code * adding change * adding logs * adding notes * making deubgging easier * adding code to fix paste * adding code to allow await before trigger paste * removing all logs * removing log * refactoring * removing change * adding fix after call * polishing * fixing merge conflict * polishing
1 parent 176cf7d commit 8aead9b

9 files changed

Lines changed: 59 additions & 26 deletions

File tree

src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { EditContext } from './editContextFactory.js';
3030
import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js';
3131
import { NativeEditContextRegistry } from './nativeEditContextRegistry.js';
3232
import { IEditorAriaOptions } from '../../../editorBrowser.js';
33+
import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js';
3334

3435
// Corresponds to classes in nativeEditContext.css
3536
enum CompositionClassName {
@@ -68,7 +69,8 @@ export class NativeEditContext extends AbstractEditContext {
6869
viewController: ViewController,
6970
private readonly _visibleRangeProvider: IVisibleRangeProvider,
7071
@IInstantiationService instantiationService: IInstantiationService,
71-
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService
72+
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
73+
@IClipboardService private readonly _clipboardService: IClipboardService,
7274
) {
7375
super(context);
7476

@@ -253,24 +255,26 @@ export class NativeEditContext extends AbstractEditContext {
253255
return true;
254256
}
255257

256-
public executePaste(): boolean {
258+
public triggerPaste(): Promise<void> | undefined {
257259
this._onWillPaste();
258-
try {
259-
// pause focus tracking because we don't want to react to focus/blur
260-
// events while pasting since we move the focus to the textarea
261-
this._focusTracker.pause();
262-
263-
// Since we can not call execCommand('paste') on a dom node with edit context set
264-
// we added a hidden text area that receives the paste execution
265-
this._textArea.focus();
266-
const result = this._textArea.domNode.ownerDocument.execCommand('paste');
267-
this._textArea.domNode.textContent = '';
268-
this.domNode.focus();
269-
270-
return result;
271-
} finally {
260+
// pause focus tracking because we don't want to react to focus/blur
261+
// events while pasting since we move the focus to the textarea
262+
this._focusTracker.pause();
263+
264+
// Since we can not call execCommand('paste') on a dom node with edit context set
265+
// we added a hidden text area that receives the paste execution
266+
this._textArea.focus();
267+
const triggerPaste = this._clipboardService.triggerPaste();
268+
if (!triggerPaste) {
269+
this.domNode.domNode.focus();
272270
this._focusTracker.resume(); // resume focus tracking
271+
return undefined;
273272
}
273+
return triggerPaste.then(() => {
274+
this._textArea.domNode.textContent = '';
275+
this.domNode.domNode.focus();
276+
this._focusTracker.resume(); // resume focus tracking
277+
});
274278
}
275279

276280
private _onWillPaste(): void {

src/vs/editor/contrib/clipboard/browser/clipboard.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,26 @@ if (PasteAction) {
234234
const focusedEditor = codeEditorService.getFocusedCodeEditor();
235235
if (focusedEditor && focusedEditor.hasModel() && focusedEditor.hasTextFocus()) {
236236
// execCommand(paste) does not work with edit context
237-
let result: boolean;
238237
const experimentalEditContextEnabled = focusedEditor.getOption(EditorOption.effectiveExperimentalEditContextEnabled);
239238
if (experimentalEditContextEnabled) {
240239
const nativeEditContext = NativeEditContextRegistry.get(focusedEditor.getId());
241240
if (nativeEditContext) {
242-
result = nativeEditContext.executePaste();
243-
} else {
244-
result = false;
241+
const triggerPaste = nativeEditContext.triggerPaste();
242+
if (triggerPaste) {
243+
return triggerPaste.then(async () => {
244+
return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve();
245+
});
246+
}
245247
}
246248
} else {
247-
result = focusedEditor.getContainerDomNode().ownerDocument.execCommand('paste');
249+
const triggerPaste = clipboardService.triggerPaste();
250+
if (triggerPaste) {
251+
return triggerPaste.then(async () => {
252+
return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve();
253+
});
254+
}
248255
}
249-
if (result) {
250-
return CopyPasteController.get(focusedEditor)?.finishedPaste() ?? Promise.resolve();
251-
} else if (platform.isWeb) {
256+
if (platform.isWeb) {
252257
// Use the clipboard service if document.execCommand('paste') was not successful
253258
return (async () => {
254259
const clipboardText = await clipboardService.readText();
@@ -278,8 +283,8 @@ if (PasteAction) {
278283

279284
// 2. Paste: (default) handle case when focus is somewhere else.
280285
PasteAction.addImplementation(0, 'generic-dom', (accessor: ServicesAccessor, args: any) => {
281-
getActiveDocument().execCommand('paste');
282-
return true;
286+
const triggerPaste = accessor.get(IClipboardService).triggerPaste();
287+
return triggerPaste ?? false;
283288
});
284289
}
285290

src/vs/platform/clipboard/browser/clipboardService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export class BrowserClipboardService extends Disposable implements IClipboardSer
4545
}, { window: mainWindow, disposables: this._store }));
4646
}
4747

48+
triggerPaste(): Promise<void> | undefined {
49+
return undefined;
50+
}
51+
4852
async readImage(): Promise<Uint8Array> {
4953
try {
5054
const clipboardItems = await navigator.clipboard.read();

src/vs/platform/clipboard/common/clipboardService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ export interface IClipboardService {
1212

1313
readonly _serviceBrand: undefined;
1414

15+
/**
16+
* Trigger the paste. Returns undefined if the paste was not triggered or a promise that resolves on paste end.
17+
*/
18+
triggerPaste(): Promise<void> | undefined;
19+
1520
/**
1621
* Writes text to the system clipboard.
1722
*/

src/vs/platform/clipboard/test/common/testClipboardService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export class TestClipboardService implements IClipboardService {
1515

1616
private text: string | undefined = undefined;
1717

18+
triggerPaste(): Promise<void> | undefined {
19+
return Promise.resolve();
20+
}
21+
1822
async writeText(text: string, type?: string): Promise<void> {
1923
this.text = text;
2024
}

src/vs/platform/native/common/native.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export interface ICommonNativeHostService {
150150
killProcess(pid: number, code: string): Promise<void>;
151151

152152
// Clipboard
153+
triggerPaste(): Promise<void>;
153154
readClipboardText(type?: 'selection' | 'clipboard'): Promise<string>;
154155
writeClipboardText(text: string, type?: 'selection' | 'clipboard'): Promise<void>;
155156
readClipboardFindText(): Promise<string>;

src/vs/platform/native/electron-main/nativeHostMainService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,11 @@ export class NativeHostMainService extends Disposable implements INativeHostMain
751751
return clipboard.readText(type);
752752
}
753753

754+
async triggerPaste(windowId: number | undefined): Promise<void> {
755+
const window = this.windowById(windowId);
756+
return window?.win?.webContents.paste() ?? Promise.resolve();
757+
}
758+
754759
async readImage(): Promise<Uint8Array> {
755760
return clipboard.readImage().toPNG();
756761
}

src/vs/workbench/services/clipboard/electron-sandbox/clipboardService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ export class NativeClipboardService implements IClipboardService {
2020
@INativeHostService private readonly nativeHostService: INativeHostService
2121
) { }
2222

23+
async triggerPaste(): Promise<void> {
24+
return this.nativeHostService.triggerPaste();
25+
}
26+
2327
async readImage(): Promise<Uint8Array> {
2428
return this.nativeHostService.readImage();
2529
}

src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export class TestNativeHostService implements INativeHostService {
156156
async readClipboardFindText(): Promise<string> { return ''; }
157157
async writeClipboardFindText(text: string): Promise<void> { }
158158
async writeClipboardBuffer(format: string, buffer: VSBuffer, type?: 'selection' | 'clipboard' | undefined): Promise<void> { }
159+
async triggerPaste(): Promise<void> { }
159160
async readImage(): Promise<Uint8Array> { return Uint8Array.from([]); }
160161
async readClipboardBuffer(format: string): Promise<VSBuffer> { return VSBuffer.wrap(Uint8Array.from([])); }
161162
async hasClipboard(format: string, type?: 'selection' | 'clipboard' | undefined): Promise<boolean> { return false; }

0 commit comments

Comments
 (0)