Skip to content

Commit

Permalink
Make Clipboard Plugin API work in FireFox
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Zatsarynnyi <azatsary@redhat.com>
  • Loading branch information
azatsarynnyy committed Sep 12, 2019
1 parent 7a77260 commit c279078
Showing 1 changed file with 72 additions and 11 deletions.
83 changes: 72 additions & 11 deletions packages/core/src/browser/browser-clipboard-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,98 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable } from 'inversify';
import { injectable, inject } from 'inversify';
import { isFirefox } from './browser';
import { ClipboardService } from './clipboard-service';
import { ILogger } from '../common/logger';
import { MessageService } from '../common/message-service';

export interface NavigatorClipboard {
readText(): Promise<string>;
writeText(value: string): Promise<void>;
}
export interface PermissionStatus {
state: 'granted' | 'prompt' | 'denied'
}
export interface NavigatorPermissions {
query(options: { name: string }): Promise<{ state: 'granted' | 'prompt' | 'denied' }>
query(options: { name: string }): Promise<PermissionStatus>
}

@injectable()
export class BrowserClipboardService implements ClipboardService {

@inject(MessageService)
protected readonly messageService: MessageService;

@inject(ILogger)
protected readonly logger: ILogger;

async readText(): Promise<string> {
if ('permissions' in navigator && 'clipboard' in navigator) {
const result = await (<NavigatorPermissions>navigator['permissions']).query({ name: 'clipboard-read' });
if (result.state === 'granted' || result.state === 'prompt') {
return (<NavigatorClipboard>navigator['clipboard']).readText();
let permission;
try {
permission = await this.queryPermission('clipboard-read');
} catch (e) {
this.logger.error('Failed checking a clipboard-read permission.', e);
// in FireFox, Clipboard API isn't gated with the permissions
try {
return await this.getClipboardAPI().readText();
} catch (e) {
this.logger.error('Failed reading clipboard content.', e);
if (isFirefox) {
this.messageService.warn(`Clipboard API is not available.
It can be enabled by 'dom.events.testing.asyncClipboard' preference on 'about:config' page. Then reload Theia.
Note, it will allow FireFox getting full access to the system clipboard.`);
}
throw new Error('Failed reading clipboard content.');
}
}
return '';
if (permission.state === 'denied') {
// most likely, the user intentionally denied the access
this.messageService.error("Access to the clipboard is denied. Check your browser's permission.");
throw new Error('Access to the clipboard is denied.');
}
return this.getClipboardAPI().readText();
}

async writeText(value: string): Promise<void> {
if ('permissions' in navigator && 'clipboard' in navigator) {
const result = await (<NavigatorPermissions>navigator['permissions']).query({ name: 'clipboard-write' });
if (result.state === 'granted' || result.state === 'prompt') {
return (<NavigatorClipboard>navigator['clipboard']).writeText(value);
let permission;
try {
permission = await this.queryPermission('clipboard-write');
} catch (e) {
this.logger.error('Failed checking a clipboard-write permission.', e);
// in FireFox, Clipboard API isn't gated with the permissions
try {
await this.getClipboardAPI().writeText(value);
return;
} catch (e) {
this.logger.error('Failed writing to the clipboard.', e);
if (isFirefox) {
this.messageService.warn(`Clipboard API is not available.
It can be enabled by 'dom.events.testing.asyncClipboard' preference on 'about:config' page. Then reload Theia.
Note, it will allow FireFox getting full access to the system clipboard.`);
}
throw new Error('Failed writing the the clipboard.');
}
}
if (permission.state === 'denied') {
// most likely, the user intentionally denied the access
this.messageService.error("Access to the clipboard is denied. Check your browser's permission.");
throw new Error('Access to the clipboard is denied.');
}
return this.getClipboardAPI().writeText(value);
}

protected async queryPermission(name: string): Promise<PermissionStatus> {
if ('permissions' in navigator) {
return (<NavigatorPermissions>navigator['permissions']).query({ name: name });
}
throw new Error('Permissions API unavailable');
}

protected getClipboardAPI(): NavigatorClipboard {
if ('clipboard' in navigator) {
return (<NavigatorClipboard>navigator['clipboard']);
}
throw new Error('Async Clipboard API unavailable');
}
}

0 comments on commit c279078

Please sign in to comment.