Add api.clipboardGetImage (#778)

* Rename clipboardPasteTarget to just target

* Remove else block

* Add helper functions

* Defer assignment of clipboard paste target

* Add api.clipboardGetImage
This commit is contained in:
toasted-nutbread 2020-09-06 14:38:03 -04:00 committed by GitHub
parent b28241dbf2
commit 115afb63b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 16 deletions

View File

@ -46,5 +46,12 @@
<script src="/mixed/js/object-property-accessor.js"></script> <script src="/mixed/js/object-property-accessor.js"></script>
<script src="/bg/js/background-main.js"></script> <script src="/bg/js/background-main.js"></script>
<!--
Due to a Firefox bug, this next element is purposefully terminated incorrectly.
This element must appear directly inside the <body> element, and it must not be closed properly.
https://bugzilla.mozilla.org/show_bug.cgi?id=1603985
-->
<div id="clipboard-image-paste-target" contenteditable="true">
</body> </body>
</html> </html>

View File

@ -64,11 +64,10 @@ class Backend {
}); });
this._templateRenderer = new TemplateRenderer(); this._templateRenderer = new TemplateRenderer();
this._clipboardPasteTarget = ( this._clipboardPasteTarget = null;
typeof document === 'object' && document !== null ? this._clipboardPasteTargetInitialized = false;
document.querySelector('#clipboard-paste-target') : this._clipboardImagePasteTarget = null;
null this._clipboardImagePasteTargetInitialized = false;
);
this._searchPopupTabId = null; this._searchPopupTabId = null;
this._searchPopupTabCreatePromise = null; this._searchPopupTabCreatePromise = null;
@ -108,6 +107,7 @@ class Backend {
['getStylesheetContent', {async: true, contentScript: true, handler: this._onApiGetStylesheetContent.bind(this)}], ['getStylesheetContent', {async: true, contentScript: true, handler: this._onApiGetStylesheetContent.bind(this)}],
['getEnvironmentInfo', {async: false, contentScript: true, handler: this._onApiGetEnvironmentInfo.bind(this)}], ['getEnvironmentInfo', {async: false, contentScript: true, handler: this._onApiGetEnvironmentInfo.bind(this)}],
['clipboardGet', {async: true, contentScript: true, handler: this._onApiClipboardGet.bind(this)}], ['clipboardGet', {async: true, contentScript: true, handler: this._onApiClipboardGet.bind(this)}],
['clipboardGetImage', {async: true, contentScript: true, handler: this._onApiClipboardImageGet.bind(this)}],
['getDisplayTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetDisplayTemplatesHtml.bind(this)}], ['getDisplayTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetDisplayTemplatesHtml.bind(this)}],
['getQueryParserTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetQueryParserTemplatesHtml.bind(this)}], ['getQueryParserTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetQueryParserTemplatesHtml.bind(this)}],
['getZoom', {async: true, contentScript: true, handler: this._onApiGetZoom.bind(this)}], ['getZoom', {async: true, contentScript: true, handler: this._onApiGetZoom.bind(this)}],
@ -645,18 +645,63 @@ class Backend {
const {browser} = this._environment.getInfo(); const {browser} = this._environment.getInfo();
if (browser === 'firefox' || browser === 'firefox-mobile') { if (browser === 'firefox' || browser === 'firefox-mobile') {
return await navigator.clipboard.readText(); return await navigator.clipboard.readText();
} else {
const clipboardPasteTarget = this._clipboardPasteTarget;
if (clipboardPasteTarget === null) {
throw new Error('Reading the clipboard is not supported in this context');
}
clipboardPasteTarget.value = '';
clipboardPasteTarget.focus();
document.execCommand('paste');
const result = clipboardPasteTarget.value;
clipboardPasteTarget.value = '';
return result;
} }
if (!this._environmentHasDocument()) {
throw new Error('Reading the clipboard is not supported in this context');
}
if (!this._clipboardPasteTargetInitialized) {
this._clipboardPasteTarget = document.querySelector('#clipboard-paste-target');
this._clipboardPasteTargetInitialized = true;
}
const target = this._clipboardPasteTarget;
if (target === null) {
throw new Error('Clipboard paste target does not exist');
}
target.value = '';
target.focus();
this._executePasteCommand();
const result = target.value;
target.value = '';
return result;
}
async _onApiClipboardImageGet() {
// See browser-specific notes in _onApiClipboardGet
const {browser} = this._environment.getInfo();
if (browser === 'firefox' || browser === 'firefox-mobile') {
if (typeof navigator.clipboard !== 'undefined' && typeof navigator.clipboard.read === 'function') {
// This function is behind the flag: dom.events.asyncClipboard.dataTransfer
const {files} = await navigator.clipboard.read();
if (files.length === 0) { return null; }
const result = await this._readFileAsDataURL(files[0]);
return result;
}
}
if (!this._environmentHasDocument()) {
throw new Error('Reading the clipboard is not supported in this context');
}
if (!this._clipboardImagePasteTargetInitialized) {
this._clipboardImagePasteTarget = document.querySelector('#clipboard-image-paste-target');
this._clipboardImagePasteTargetInitialized = true;
}
const target = this._clipboardImagePasteTarget;
if (target === null) {
throw new Error('Clipboard paste target does not exist');
}
target.focus();
this._executePasteCommand();
const image = target.querySelector('img[src^="data:"]');
const result = (image !== null ? image.getAttribute('src') : null);
target.textContent = '';
return result;
} }
async _onApiGetDisplayTemplatesHtml() { async _onApiGetDisplayTemplatesHtml() {
@ -1539,4 +1584,21 @@ class Backend {
const isValidTab = urlPredicate(url); const isValidTab = urlPredicate(url);
return isValidTab ? tab : null; return isValidTab ? tab : null;
} }
_environmentHasDocument() {
return (typeof document === 'object' && document !== null);
}
_executePasteCommand() {
document.execCommand('paste');
}
_readFileAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
} }

View File

@ -133,6 +133,10 @@ const api = (() => {
return this._invoke('clipboardGet'); return this._invoke('clipboardGet');
} }
clipboardGetImage() {
return this._invoke('clipboardGetImage');
}
getDisplayTemplatesHtml() { getDisplayTemplatesHtml() {
return this._invoke('getDisplayTemplatesHtml'); return this._invoke('getDisplayTemplatesHtml');
} }