Clipboard updates (#2254)

* Rename

* Rename vars

* Refactor paste target

* Prevent most CSS url() properties from loading

* Add helper function to clear rich function

* Add useRichText argument

* Update condition for using readText

* Fix indent

* Update CSS
This commit is contained in:
toasted-nutbread 2022-10-15 22:43:12 -04:00 committed by GitHub
parent a370b46fae
commit 9ef7f9d383
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 39 deletions

View File

@ -11,6 +11,7 @@
<link rel="icon" type="image/png" href="/images/icon48.png" sizes="48x48"> <link rel="icon" type="image/png" href="/images/icon48.png" sizes="48x48">
<link rel="icon" type="image/png" href="/images/icon64.png" sizes="64x64"> <link rel="icon" type="image/png" href="/images/icon64.png" sizes="64x64">
<link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128"> <link rel="icon" type="image/png" href="/images/icon128.png" sizes="128x128">
<link rel="stylesheet" type="text/css" href="/css/background.css">
</head> </head>
<body> <body>
@ -61,7 +62,7 @@
https://bugzilla.mozilla.org/show_bug.cgi?id=1603985 https://bugzilla.mozilla.org/show_bug.cgi?id=1603985
--> -->
<!-- [html-validate-disable close-order] --> <!-- [html-validate-disable close-order] -->
<div id="clipboard-image-paste-target" contenteditable="true"> <div id="clipboard-rich-content-paste-target" contenteditable="true">
</body> </body>
</html> </html>

29
ext/css/background.css Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2022 Yomichan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* stylelint-disable declaration-no-important */
#clipboard-rich-content-paste-target * {
background-image: none !important;
list-style-image: none !important;
content: none !important;
cursor: auto !important;
border-image-source: none !important;
offset-path: none !important;
-webkit-mask-image: none !important;
mask-image: none !important;
}
/* stylelint-enable declaration-no-important */

View File

@ -53,7 +53,7 @@ class Backend {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
document: (typeof document === 'object' && document !== null ? document : null), document: (typeof document === 'object' && document !== null ? document : null),
pasteTargetSelector: '#clipboard-paste-target', pasteTargetSelector: '#clipboard-paste-target',
imagePasteTargetSelector: '#clipboard-image-paste-target' richContentPasteTargetSelector: '#clipboard-rich-content-paste-target'
}); });
this._clipboardMonitor = new ClipboardMonitor({ this._clipboardMonitor = new ClipboardMonitor({
japaneseUtil: this._japaneseUtil, japaneseUtil: this._japaneseUtil,
@ -596,7 +596,7 @@ class Backend {
} }
async _onApiClipboardGet() { async _onApiClipboardGet() {
return this._clipboardReader.getText(); return this._clipboardReader.getText(false);
} }
async _onApiGetDisplayTemplatesHtml() { async _onApiGetDisplayTemplatesHtml() {
@ -1773,7 +1773,7 @@ class Backend {
try { try {
if (clipboardDetails !== null && clipboardDetails.text) { if (clipboardDetails !== null && clipboardDetails.text) {
clipboardText = await this._clipboardReader.getText(); clipboardText = await this._clipboardReader.getText(false);
} }
} catch (e) { } catch (e) {
errors.push(serializeError(e)); errors.push(serializeError(e));

View File

@ -39,7 +39,7 @@ class ClipboardMonitor extends EventDispatcher {
let text = null; let text = null;
try { try {
text = await this._clipboardReader.getText(); text = await this._clipboardReader.getText(false);
} catch (e) { } catch (e) {
// NOP // NOP
} }

View File

@ -28,15 +28,15 @@ class ClipboardReader {
* @param {object} details Details about how to set up the instance. * @param {object} details Details about how to set up the instance.
* @param {?Document} details.document The Document object to be used, or null for no support. * @param {?Document} details.document The Document object to be used, or null for no support.
* @param {?string} details.pasteTargetSelector The selector for the paste target element. * @param {?string} details.pasteTargetSelector The selector for the paste target element.
* @param {?string} details.imagePasteTargetSelector The selector for the image paste target element. * @param {?string} details.richContentPasteTargetSelector The selector for the rich content paste target element.
*/ */
constructor({document=null, pasteTargetSelector=null, imagePasteTargetSelector=null}) { constructor({document=null, pasteTargetSelector=null, richContentPasteTargetSelector=null}) {
this._document = document; this._document = document;
this._browser = null; this._browser = null;
this._pasteTarget = null; this._pasteTarget = null;
this._pasteTargetSelector = pasteTargetSelector; this._pasteTargetSelector = pasteTargetSelector;
this._imagePasteTarget = null; this._richContentPasteTarget = null;
this._imagePasteTargetSelector = imagePasteTargetSelector; this._richContentPasteTargetSelector = richContentPasteTargetSelector;
} }
/** /**
@ -56,13 +56,14 @@ class ClipboardReader {
/** /**
* Gets the text in the clipboard. * Gets the text in the clipboard.
* @param {boolean} useRichText Whether or not to use rich text for pasting, when possible.
* @returns {string} A string containing the clipboard text. * @returns {string} A string containing the clipboard text.
* @throws {Error} Error if not supported. * @throws {Error} Error if not supported.
*/ */
async getText() { async getText(useRichText) {
/* /*
Notes: Notes:
document.execCommand('paste') doesn't work on Firefox. document.execCommand('paste') sometimes doesn't work on Firefox.
See: https://bugzilla.mozilla.org/show_bug.cgi?id=1603985 See: https://bugzilla.mozilla.org/show_bug.cgi?id=1603985
Therefore, navigator.clipboard.readText() is used on Firefox. Therefore, navigator.clipboard.readText() is used on Firefox.
@ -72,7 +73,7 @@ class ClipboardReader {
being an extension with clipboard permissions. It effectively asks for the being an extension with clipboard permissions. It effectively asks for the
non-extension permission for clipboard access. non-extension permission for clipboard access.
*/ */
if (this._isFirefox()) { if (this._isFirefox() && !useRichText) {
try { try {
return await navigator.clipboard.readText(); return await navigator.clipboard.readText();
} catch (e) { } catch (e) {
@ -86,15 +87,15 @@ class ClipboardReader {
throw new Error('Clipboard reading not supported in this context'); throw new Error('Clipboard reading not supported in this context');
} }
let target = this._pasteTarget; if (useRichText) {
if (target === null) { const target = this._getRichContentPasteTarget();
target = document.querySelector(this._pasteTargetSelector); target.focus();
if (target === null) { document.execCommand('paste');
throw new Error('Clipboard paste target does not exist'); const result = target.textContent;
} this._clearRichContent(target);
this._pasteTarget = target; return result;
} } else {
const target = this._getPasteTarget();
target.value = ''; target.value = '';
target.focus(); target.focus();
document.execCommand('paste'); document.execCommand('paste');
@ -102,6 +103,7 @@ class ClipboardReader {
target.value = ''; target.value = '';
return (typeof result === 'string' ? result : ''); return (typeof result === 'string' ? result : '');
} }
}
/** /**
* Gets the first image in the clipboard. * Gets the first image in the clipboard.
@ -143,23 +145,12 @@ class ClipboardReader {
throw new Error('Clipboard reading not supported in this context'); throw new Error('Clipboard reading not supported in this context');
} }
let target = this._imagePasteTarget; const target = this._getRichContentPasteTarget();
if (target === null) {
target = document.querySelector(this._imagePasteTargetSelector);
if (target === null) {
throw new Error('Clipboard paste target does not exist');
}
this._imagePasteTarget = target;
}
target.focus(); target.focus();
document.execCommand('paste'); document.execCommand('paste');
const image = target.querySelector('img[src^="data:"]'); const image = target.querySelector('img[src^="data:"]');
const result = (image !== null ? image.getAttribute('src') : null); const result = (image !== null ? image.getAttribute('src') : null);
for (const image2 of target.querySelectorAll('img')) { this._clearRichContent(target);
image2.removeAttribute('src');
}
target.textContent = '';
return result; return result;
} }
@ -177,4 +168,28 @@ class ClipboardReader {
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
} }
_getPasteTarget() {
if (this._pasteTarget === null) { this._pasteTarget = this._findPasteTarget(this._pasteTargetSelector); }
return this._pasteTarget;
}
_getRichContentPasteTarget() {
if (this._richContentPasteTarget === null) { this._richContentPasteTarget = this._findPasteTarget(this._richContentPasteTargetSelector); }
return this._richContentPasteTarget;
}
_findPasteTarget(selector) {
const target = this._document.querySelector(selector);
if (target === null) { throw new Error('Clipboard paste target does not exist'); }
return target;
}
_clearRichContent(element) {
for (const image of element.querySelectorAll('img')) {
image.removeAttribute('src');
image.removeAttribute('srcset');
}
element.textContent = '';
}
} }