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 | |
| 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
| -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');          } |