From 5fab02c3e36a2d2c361f3d93722a3fabe5666893 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 30 Jun 2017 12:49:11 +0200 Subject: [PATCH] Fixes Microsoft/monaco-editor#391: Have Cut and Copy in the context menu for browsers where it works --- src/vs/base/browser/ui/actionbar/actionbar.ts | 16 ++++++- .../contrib/clipboard/browser/clipboard.ts | 48 +++++++++---------- .../contextmenu/browser/contextmenu.ts | 2 +- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index ca8e4ff6837..809a57ddf72 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -32,6 +32,7 @@ export interface IActionItem extends IEventEmitter { export interface IBaseActionItemOptions { draggable?: boolean; + isMenu?: boolean; } export class BaseActionItem extends EventEmitter implements IActionItem { @@ -129,7 +130,19 @@ export class BaseActionItem extends EventEmitter implements IActionItem { this.builder.on(DOM.EventType.CLICK, (e: MouseEvent) => { DOM.EventHelper.stop(e, true); - setTimeout(() => this.onClick(e), 50); + // See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard + // > Writing to the clipboard + // > You can use the "cut" and "copy" commands without any special + // permission if you are using them in a short-lived event handler + // for a user action (for example, a click handler). + + // => to get the Copy and Paste context menu actions working on Firefox, + // there should be no timeout here + if (this.options && this.options.isMenu) { + this.onClick(e); + } else { + setTimeout(() => this.onClick(e), 50); + } }); this.builder.on([DOM.EventType.MOUSE_UP, DOM.EventType.MOUSE_OUT], (e: MouseEvent) => { @@ -218,7 +231,6 @@ export interface IActionItemOptions extends IBaseActionItemOptions { icon?: boolean; label?: boolean; keybinding?: string; - isMenu?: boolean; } export class ActionItem extends BaseActionItem { diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts index c09a2408969..84cb6cc3167 100644 --- a/src/vs/editor/contrib/clipboard/browser/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -19,33 +19,29 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; -function supportsExecCommand(command: string): boolean { - return ( - (browser.isIE || platform.isNative) - && document.queryCommandSupported(command) - ); -} - -function conditionalEditorAction(testCommand: string) { - if (!supportsExecCommand(testCommand)) { - return () => { }; - } - return editorAction; -} - -function conditionalCopyWithSyntaxHighlighting() { - if (browser.isEdgeOrIE || !supportsExecCommand('copy')) { +const supportsCut = (platform.isNative || document.queryCommandSupported('cut')); +const supportsCopy = (platform.isNative || document.queryCommandSupported('copy')); +// IE and Edge have trouble with setting html content in clipboard +const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE); +// Chrome incorrectly returns true for document.queryCommandSupported('paste') +// when the paste feature is available but the calling script has insufficient +// privileges to actually perform the action +const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste'))); + +type ExecCommand = 'cut' | 'copy' | 'paste'; + +function conditionalEditorAction(condition: boolean) { + if (!condition) { return () => { }; } - return editorAction; } abstract class ExecCommandAction extends EditorAction { - private browserCommand: string; + private browserCommand: ExecCommand; - constructor(browserCommand: string, opts: IActionOptions) { + constructor(browserCommand: ExecCommand, opts: IActionOptions) { super(opts); this.browserCommand = browserCommand; } @@ -67,7 +63,7 @@ abstract class ExecCommandAction extends EditorAction { } } -@conditionalEditorAction('cut') +@conditionalEditorAction(supportsCut) class ExecCommandCutAction extends ExecCommandAction { constructor() { @@ -78,7 +74,7 @@ class ExecCommandCutAction extends ExecCommandAction { }; // Do not bind cut keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (browser.isIE) { + if (!platform.isNative) { kbOpts = null; } super('cut', { @@ -105,7 +101,7 @@ class ExecCommandCutAction extends ExecCommandAction { } } -@conditionalEditorAction('copy') +@conditionalEditorAction(supportsCopy) class ExecCommandCopyAction extends ExecCommandAction { constructor() { @@ -116,7 +112,7 @@ class ExecCommandCopyAction extends ExecCommandAction { }; // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (!browser.isIE) { + if (!platform.isNative) { kbOpts = null; } @@ -144,7 +140,7 @@ class ExecCommandCopyAction extends ExecCommandAction { } } -@conditionalEditorAction('paste') +@conditionalEditorAction(supportsPaste) class ExecCommandPasteAction extends ExecCommandAction { constructor() { @@ -155,7 +151,7 @@ class ExecCommandPasteAction extends ExecCommandAction { }; // Do not bind paste keybindings in the browser, // since browsers do that for us and it avoids security prompts - if (!browser.isIE) { + if (!platform.isNative) { kbOpts = null; } @@ -173,7 +169,7 @@ class ExecCommandPasteAction extends ExecCommandAction { } } -@conditionalCopyWithSyntaxHighlighting() +@conditionalEditorAction(supportsCopyWithSyntaxHighlighting) class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { constructor() { diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index b389061785a..57414e16494 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -183,7 +183,7 @@ export class ContextMenuController implements IEditorContribution { return customActionItem.getActionItem(); } - return null; + return new ActionItem(action, action, { icon: true, label: true, isMenu: true }); }, getKeyBinding: (action): ResolvedKeybinding => { -- GitLab