diff --git a/src/vs/platform/clipboard/browser/clipboardService.ts b/src/vs/platform/clipboard/browser/clipboardService.ts index d22b7bb5bc0eb..307afd4428e25 100644 --- a/src/vs/platform/clipboard/browser/clipboardService.ts +++ b/src/vs/platform/clipboard/browser/clipboardService.ts @@ -15,6 +15,13 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; +/** + * Custom mime type used for storing a list of uris in the clipboard. + * + * Requires support for custom web clipboards https://github.com/w3c/clipboard-apis/pull/175 + */ +const vscodeResourcesMime = 'application/vnd.code.resources'; + export class BrowserClipboardService extends Disposable implements IClipboardService { declare readonly _serviceBrand: undefined; @@ -172,6 +179,26 @@ export class BrowserClipboardService extends Disposable implements IClipboardSer private static readonly MAX_RESOURCE_STATE_SOURCE_LENGTH = 1000; async writeResources(resources: URI[]): Promise { + // Guard access to navigator.clipboard with try/catch + // as we have seen DOMExceptions in certain browsers + // due to security policies. + try { + await getActiveWindow().navigator.clipboard.write([ + new ClipboardItem({ + [`web ${vscodeResourcesMime}`]: new Blob([ + JSON.stringify(resources.map(x => x.toJSON())) + ], { + type: vscodeResourcesMime + }) + }) + ]); + + // Continue to write to the in-memory clipboard as well. + // This is needed because some browsers allow the paste but then can't read the custom resources. + } catch (error) { + // Noop + } + if (resources.length === 0) { this.clearResources(); } else { @@ -181,6 +208,22 @@ export class BrowserClipboardService extends Disposable implements IClipboardSer } async readResources(): Promise { + // Guard access to navigator.clipboard with try/catch + // as we have seen DOMExceptions in certain browsers + // due to security policies. + try { + const items = await getActiveWindow().navigator.clipboard.read(); + for (const item of items) { + if (item.types.includes(`web ${vscodeResourcesMime}`)) { + const blob = await item.getType(`web ${vscodeResourcesMime}`); + const resources = (JSON.parse(await blob.text()) as URI[]).map(x => URI.from(x)); + return resources; + } + } + } catch (error) { + // Noop + } + const resourcesStateHash = await this.computeResourcesStateHash(); if (this.resourcesStateHash !== resourcesStateHash) { this.clearResources(); // state mismatch, resources no longer valid @@ -204,6 +247,20 @@ export class BrowserClipboardService extends Disposable implements IClipboardSer } async hasResources(): Promise { + // Guard access to navigator.clipboard with try/catch + // as we have seen DOMExceptions in certain browsers + // due to security policies. + try { + const items = await getActiveWindow().navigator.clipboard.read(); + for (const item of items) { + if (item.types.includes(`web ${vscodeResourcesMime}`)) { + return true; + } + } + } catch (error) { + // Noop + } + return this.resources.length > 0; }