diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-09-06 14:38:03 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-06 14:38:03 -0400 |
commit | 115afb63b90b8e6841061bbf8d67e82cab42958b (patch) | |
tree | 9eebcad5518f3ec1f759e0632044e6c987a6aa7e /ext | |
parent | b28241dbf23b627e252348aa0445e4e7befe01fc (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
Diffstat (limited to 'ext')
-rw-r--r-- | ext/bg/background.html | 7 | ||||
-rw-r--r-- | ext/bg/js/backend.js | 92 | ||||
-rw-r--r-- | ext/mixed/js/api.js | 4 |
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'); } |