aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-09-06 14:38:03 -0400
committerGitHub <noreply@github.com>2020-09-06 14:38:03 -0400
commit115afb63b90b8e6841061bbf8d67e82cab42958b (patch)
tree9eebcad5518f3ec1f759e0632044e6c987a6aa7e
parentb28241dbf23b627e252348aa0445e4e7befe01fc (diff)
Add api.clipboardGetImage (#778)
* Rename clipboardPasteTarget to just target * Remove else block * Add helper functions * Defer assignment of clipboard paste target * Add api.clipboardGetImage
-rw-r--r--ext/bg/background.html7
-rw-r--r--ext/bg/js/backend.js92
-rw-r--r--ext/mixed/js/api.js4
3 files changed, 88 insertions, 15 deletions
diff --git a/ext/bg/background.html b/ext/bg/background.html
index ba8c3863..3b889fb8 100644
--- a/ext/bg/background.html
+++ b/ext/bg/background.html
@@ -46,5 +46,12 @@
<script src="/mixed/js/object-property-accessor.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>
</html>
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index a9ef4ce4..047044df 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -64,11 +64,10 @@ class Backend {
});
this._templateRenderer = new TemplateRenderer();
- this._clipboardPasteTarget = (
- typeof document === 'object' && document !== null ?
- document.querySelector('#clipboard-paste-target') :
- null
- );
+ this._clipboardPasteTarget = null;
+ this._clipboardPasteTargetInitialized = false;
+ this._clipboardImagePasteTarget = null;
+ this._clipboardImagePasteTargetInitialized = false;
this._searchPopupTabId = null;
this._searchPopupTabCreatePromise = null;
@@ -108,6 +107,7 @@ class Backend {
['getStylesheetContent', {async: true, contentScript: true, handler: this._onApiGetStylesheetContent.bind(this)}],
['getEnvironmentInfo', {async: false, contentScript: true, handler: this._onApiGetEnvironmentInfo.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)}],
['getQueryParserTemplatesHtml', {async: true, contentScript: true, handler: this._onApiGetQueryParserTemplatesHtml.bind(this)}],
['getZoom', {async: true, contentScript: true, handler: this._onApiGetZoom.bind(this)}],
@@ -645,18 +645,63 @@ class Backend {
const {browser} = this._environment.getInfo();
if (browser === 'firefox' || browser === 'firefox-mobile') {
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');
+ }
+
+ 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;
}
- 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._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() {
@@ -1539,4 +1584,21 @@ class Backend {
const isValidTab = urlPredicate(url);
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);
+ });
+ }
}
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js
index a6ac227e..63b3a3c0 100644
--- a/ext/mixed/js/api.js
+++ b/ext/mixed/js/api.js
@@ -133,6 +133,10 @@ const api = (() => {
return this._invoke('clipboardGet');
}
+ clipboardGetImage() {
+ return this._invoke('clipboardGetImage');
+ }
+
getDisplayTemplatesHtml() {
return this._invoke('getDisplayTemplatesHtml');
}